我试图从我的Haskell代码构建一个Windows DLL.应该从C#中的托管代码调用此DLL中的函数.并且,从该DLL中的函数调用至少一个函数(在c#代码中定义).
冒着过度解释的风险,这是一个描绘我想要的小图:
+----------------------+ +------------------------+
| Managed C# code | | Haskell code (in DLL) |
| | (1) | |
| fn_calling_hs() -----------------> fn_called_from_cs() |
| | | |
| | | |
| fn_called_from_hs() <--------------- fn_calling_cs() |
| | (2) | |
+----------------------+ +------------------------+
Run Code Online (Sandbox Code Playgroud)
我设法使(1)完美地工作,即,DLL中的Haskell函数由C#代码调用,具有正确的结构和数组编组,并且Haskell中函数执行的结果也是正确的.到现在为止还挺好.
问题在于(2),即来自Haskell(在DLL中)调用C#中定义的托管函数的函数.问题在于构建本身 - 我还没有过去实际检查(2)的结果.
由于c#托管代码中的fn_called_from_hs()是在C#中定义的,因此我只在Haskell代码(在DLL中)中使用了函数符号"imported":
foreign import ccall fn_called_from_hs :: IO CString
Run Code Online (Sandbox Code Playgroud)
现在,当我使用堆栈构建我的Haskell项目时,它构建Haskell DLL没有问题,但构建继续也链接"main.exe" - 这显然失败了(显然),因为没有在任何地方定义的函数fn_called_from_hs() Haskell代码(它在c#中定义).
在构建HsDLL.dll之后,有什么办法可以阻止堆栈继续构建main.exe吗?我对HsDLL.dll具有未解析的符号(fn_called_from_hs())没问题,因为在由管理的C#代码加载此DLL期间,运行时链接程序将找到此符号.
到目前为止,我已尝试过这些步骤,但它们都没有帮助:
-no-hs-main在package.yaml中添加了GHC选项:包含HsDLL构建的package.yaml部分如下所示:
library:
source-dirs:
- src
- src/csrc
include-dirs: src/csrc
ghc-options:
- -shared
- …Run Code Online (Sandbox Code Playgroud)假设你有一个C结构
typedef struct {
uint32_t num;
char* str;
} MyStruct;
Run Code Online (Sandbox Code Playgroud)
以及对其f执行某些操作的函数,
void f(MyStruct* p);
Run Code Online (Sandbox Code Playgroud)
C API要求char*在调用之前分配足够的缓冲区f:
char buf[64]; //the C API docs say 64
MyStruct s = {1, buf};
f(s); // would go badly if MyStruct.str isn't alloc'ed
Run Code Online (Sandbox Code Playgroud)
(请注意,该num字段在此构造示例中没有任何用途.它只是防止使用CString和的简单解决方案CStringLen.)
问题是如何为这种C API编写Haskell FFI.
我想出的是:开始
data MyStruct = MyStruct {
num :: Word32,
str :: String
} deriving Show
Run Code Online (Sandbox Code Playgroud)
并写一个可存储的实例.我的想法是在末尾分配64个字节,它将作为字符串的缓冲区:
instance Storable MyStruct where
sizeOf _ = 8{-alignment!-} + sizeOf …Run Code Online (Sandbox Code Playgroud) 我已将“NumericalIntegration”C++ 库封装在 Haskell 中。这是该软件包的最新版本(Hackage 上的版本较旧)。
这是 C++ 代码的主要部分:
class Integrand {
private:
std::function<double(double)> f;
public:
Integrand(std::function<double(double)>& f_) : f(f_) {}
double operator()(const double x) const { return f(x); }
};
double integration(double f(double),
double lower,
double upper,
double relError,
int subdiv,
double* errorEstimate,
int* errorCode) {
// Define the integrand.
std::function<double(double)> f_ = [&](double x) { return f(x); };
Integrand integrand(f_);
// Define the integrator.
Eigen::Integrator<double> integrator(subdiv);
// Define a quadrature rule.
Eigen::Integrator<double>::QuadratureRule rule =
Eigen::Integrator<double>::GaussKronrod201;
// Define …Run Code Online (Sandbox Code Playgroud) 假设使用在 C 头文件中初始化的变量的每个源 c 文件都使用 声明extern,则无论调用源 c 文件中定义的函数(包括该头文件并声明该变量)多少次,该变量都应该只分配一次extern。
因此,我指的是一种方法,以确保不会为使用该常量的任何函数的每次调用分配单独文件中定义的函数之间使用的常量。
现在,当我在这些条件下使用 Haskell FFI 调用头文件中的函数时,我想知道调用该头文件中声明的函数是否会重复分配该变量,或者是否分配了一次。如果它不只分配一次,是否有任何简单的方法来确保它是?
以下是 .hs、.h 和 .c 文件的示例。
主要.hs:
{-# LANGUAGE ForeignFunctionInterface #-}
module Main (main) where
foreign import ccall unsafe "cstuff.h cfnc"
cfnc :: IO ()
main :: IO ()
main = do cfnc
cfnc
cfnc
Run Code Online (Sandbox Code Playgroud)
cstuff.h:
extern int myvar;
void cfnc();
Run Code Online (Sandbox Code Playgroud)
cstuff.c:
#include "cstuff.h"
#include <stdio.h>
int myvar = 1;
void cfnc() {
printf("%i\n",myvar);
}
Run Code Online (Sandbox Code Playgroud)
所以在这种情况下 cfnc 被调用了 3 次,我想知道是否myvar只分配一次或 3 次。如果它不仅仅是一次,我该怎么做才能确保它只分配一次?
让我们在 Haskell 中使用一些有限递归数据结构。例如。
data Tree = Node Tree Tree | Nil
Run Code Online (Sandbox Code Playgroud)
我需要能够将这样的数据结构从 Haskell 加载到 Python,更改它并将其返回到 Haskell。
有没有一些标准/优雅的方法可以在没有太多痛苦的情况下做到这一点?例如。使用一些目录之类的对象?