静态 link C++ 库和 Haskell 库
Statically link C++ library with a Haskell library
设置:我有一个 Haskell 库 HLib
,它调用 C/C++ 后端 CLib
以提高效率。后端很小,专门用于 HLib
。 CLib
的接口将 只有 通过 HLib
公开; HLib
测试、HLib
基准测试和依赖于 HLib
的第三方库不会对 CLib
进行直接 FFI 调用。从test/benchmark/3rd方库的角度来看,HLib
应该是纯粹的Haskell。这意味着在 cabal 文件部分中,例如 HLib
测试,不应该有对 -lCLib
、libCLib
等的引用,只有 HLib
上的 build-depends
],并且可执行文件不需要寻找动态 CLib
库。我需要能够构建 运行 HLib
和第三方库中的所有可执行文件,以及 运行 cabal repl
用于开发。
最初,CLib
是用纯 C 语言编写的。Cabal 支持这种情况,我可以通过使用 [= cabal 文件中的 33=、c-sources
和 includes
字段。
CLib
已经演变成一个 C++ 库,我不知道如何让 cabal 轻松集成。相反,我求助于带有自定义构建和 Setup.hs 的 makefile,例如 this. You can see a small example of this method here1,2.
在那个例子中,我不能在 HLib
中 运行 cabal repl
因为 "Loading archives not supported"。这实际上意味着我需要一个动态 C++ 库,它很容易创建(CLib
makefile 中有一个注释行可以创建)。但是,如果我创建动态 C++ 库,HLib
的测试会在 运行 时因 "no such file or directory libclib.so" 而失败。这很糟糕(除了崩溃之外),因为测试可执行文件链接到动态库,这不是我想要的。
具体来说,HLib
和 SimpleLib
的测试应该都通过了,我应该能够 运行 cabal repl
hlib
和simplelib
个目录。
我尝试过的其他事情:this answer, this answer (which I can't get to compile), this, and reading the docs(导致 "relocation" 错误)。
我目前正在使用 GHC-7.10.3,不过如果这在 8.0 中明显更容易,那很好。
[1] 简化自 lol/challenges.
[2]下载并运行./sandbox-init
。这将构建 HLib
(隐式构建 CLib
和 SimpleLib
,这是一个依赖于 HLib
.[=79 的 Haskell 库=]
GHC 无法真正理解 C++ 头文件。它需要纯 C 代码。 C++头文件提供C接口的常用方法是用#ifdef __cplusplus
隔离掉C++部分,例如:
#ifdef __cplusplus
extern "C" { // C compilers and various C-based FFIs don't like this
#endif
void foo();
#ifdef __cplusplus
}
#endif
此外,众所周知,GHCi 在链接 C++ 代码时存在问题。例如,它一度无法理解弱符号(通常由编译器结合内联函数和模板实例化生成)。您可能会遇到其中一个问题。我建议向 GHC 团队提交错误报告。
一旦掌握了一些技巧,将 C 或 C++ 库与 Haskell 库结合起来就很简单了。
我从这个 article 中得到了核心,尽管它似乎使事情变得过于复杂。您可以使用具有 Simple
构建类型(即没有特殊 Setup.hs
)的 cabal(当前为 1.25),没有 makefile,也没有像 c2hs
.
这样的外部工具
要包含来自纯 C 库的符号:
- 在您的 cabal 文件中,添加
Include-dirs: relative/path/to/headers/
或 Includes: relative/path/to/myheader.h
。
- 添加
C-sources: relative/path/to/csources/c1.c, relative/path/to/csources/c2.c, etc
.
C++ 有几个额外的位:
- 您可以将
.cpp
文件添加到 cabal 文件中的 C-sources
字段。
- 在 Haskell 需要访问的
.cpp
文件中的所有函数上,添加 extern "C"
以避免名称混淆。
- 用
#ifdef __cplusplus ... #endif
包围头文件中的所有非纯 C 代码(参见 n.m。的回答)。
- 如果您使用标准 C++ 库,则需要将
extra-libraries: stdc++
添加到您的 cabal 文件中,并使用 ghc-options: -pgmlg++
添加 link 和 g++
。
- 如果您想要动态 [=65],您可能需要 fiddle 使用 命令 列出 cabal 文件中的
.c(pp)
文件=]ing(即 cabal repl
)工作。有关详细信息,请参阅 this ticket。
就是这样!您可以看到一个完整的工作示例 here,它适用于 stack
和 cabal
。
设置:我有一个 Haskell 库 HLib
,它调用 C/C++ 后端 CLib
以提高效率。后端很小,专门用于 HLib
。 CLib
的接口将 只有 通过 HLib
公开; HLib
测试、HLib
基准测试和依赖于 HLib
的第三方库不会对 CLib
进行直接 FFI 调用。从test/benchmark/3rd方库的角度来看,HLib
应该是纯粹的Haskell。这意味着在 cabal 文件部分中,例如 HLib
测试,不应该有对 -lCLib
、libCLib
等的引用,只有 HLib
上的 build-depends
],并且可执行文件不需要寻找动态 CLib
库。我需要能够构建 运行 HLib
和第三方库中的所有可执行文件,以及 运行 cabal repl
用于开发。
最初,CLib
是用纯 C 语言编写的。Cabal 支持这种情况,我可以通过使用 [= cabal 文件中的 33=、c-sources
和 includes
字段。
CLib
已经演变成一个 C++ 库,我不知道如何让 cabal 轻松集成。相反,我求助于带有自定义构建和 Setup.hs 的 makefile,例如 this. You can see a small example of this method here1,2.
在那个例子中,我不能在 HLib
中 运行 cabal repl
因为 "Loading archives not supported"。这实际上意味着我需要一个动态 C++ 库,它很容易创建(CLib
makefile 中有一个注释行可以创建)。但是,如果我创建动态 C++ 库,HLib
的测试会在 运行 时因 "no such file or directory libclib.so" 而失败。这很糟糕(除了崩溃之外),因为测试可执行文件链接到动态库,这不是我想要的。
具体来说,HLib
和 SimpleLib
的测试应该都通过了,我应该能够 运行 cabal repl
hlib
和simplelib
个目录。
我尝试过的其他事情:this answer, this answer (which I can't get to compile), this, and reading the docs(导致 "relocation" 错误)。
我目前正在使用 GHC-7.10.3,不过如果这在 8.0 中明显更容易,那很好。
[1] 简化自 lol/challenges.
[2]下载并运行./sandbox-init
。这将构建 HLib
(隐式构建 CLib
和 SimpleLib
,这是一个依赖于 HLib
.[=79 的 Haskell 库=]
GHC 无法真正理解 C++ 头文件。它需要纯 C 代码。 C++头文件提供C接口的常用方法是用#ifdef __cplusplus
隔离掉C++部分,例如:
#ifdef __cplusplus
extern "C" { // C compilers and various C-based FFIs don't like this
#endif
void foo();
#ifdef __cplusplus
}
#endif
此外,众所周知,GHCi 在链接 C++ 代码时存在问题。例如,它一度无法理解弱符号(通常由编译器结合内联函数和模板实例化生成)。您可能会遇到其中一个问题。我建议向 GHC 团队提交错误报告。
一旦掌握了一些技巧,将 C 或 C++ 库与 Haskell 库结合起来就很简单了。
我从这个 article 中得到了核心,尽管它似乎使事情变得过于复杂。您可以使用具有 Simple
构建类型(即没有特殊 Setup.hs
)的 cabal(当前为 1.25),没有 makefile,也没有像 c2hs
.
要包含来自纯 C 库的符号:
- 在您的 cabal 文件中,添加
Include-dirs: relative/path/to/headers/
或Includes: relative/path/to/myheader.h
。 - 添加
C-sources: relative/path/to/csources/c1.c, relative/path/to/csources/c2.c, etc
.
C++ 有几个额外的位:
- 您可以将
.cpp
文件添加到 cabal 文件中的C-sources
字段。 - 在 Haskell 需要访问的
.cpp
文件中的所有函数上,添加extern "C"
以避免名称混淆。 - 用
#ifdef __cplusplus ... #endif
包围头文件中的所有非纯 C 代码(参见 n.m。的回答)。 - 如果您使用标准 C++ 库,则需要将
extra-libraries: stdc++
添加到您的 cabal 文件中,并使用ghc-options: -pgmlg++
添加 link 和g++
。 - 如果您想要动态 [=65],您可能需要 fiddle 使用 命令 列出 cabal 文件中的
.c(pp)
文件=]ing(即cabal repl
)工作。有关详细信息,请参阅 this ticket。
就是这样!您可以看到一个完整的工作示例 here,它适用于 stack
和 cabal
。