C# 在C语言中使用高阶Haskell类型#
如何使用和调用具有来自C#(DLLImport)的高阶类型签名的Haskell函数,例如 C#中对应的类型签名是什么 此外(因为这可能更容易):我如何在C#中使用“未知”Haskell类型,这样我至少可以在C#不知道任何特定类型的情况下传递它们?我需要知道的最重要的功能是传递类型类(如Monad或Arrow)C# 在C语言中使用高阶Haskell类型#,c#,haskell,ffi,C#,Haskell,Ffi,如何使用和调用具有来自C#(DLLImport)的高阶类型签名的Haskell函数,例如 C#中对应的类型签名是什么 此外(因为这可能更容易):我如何在C#中使用“未知”Haskell类型,这样我至少可以在C#不知道任何特定类型的情况下传递它们?我需要知道的最重要的功能是传递类型类(如Monad或Arrow) 我已经知道并在C#中使用过,但只适用于一阶函数。我也知道,而且,我没有找到任何文档和示例(针对C#to Haskell方向)。您是否尝试通过导出导出函数?这允许您为函数创建一个更加C-is
我已经知道并在C#中使用过,但只适用于一阶函数。我也知道,而且,我没有找到任何文档和示例(针对C#to Haskell方向)。您是否尝试通过导出导出函数?这允许您为函数创建一个更加C-ish的接口。我怀疑直接从C#调用Haskell函数是否可能。有关更多信息,请参阅文档。(上面的链接)
在做了一些测试之后,我认为通常情况下,不可能通过FFI导出高阶函数和带有类型参数的函数。[需要引用]好的,多亏了FuzzXL,他为“未知类型”提出了一个解决方案。将数据存储在IO上下文中的HaskellMVar中,并使用一阶函数从C#到Haskell进行通信。至少在简单的情况下,这可能是一个解决方案。我将在这里详细阐述我对Fuzzxl帖子的评论。
您发布的示例都可以使用
FFI
。一旦使用FFI导出函数,您就可以将程序编译成DLL
net是为了能够很容易地与C、C++、COM等接口而设计的,这意味着一旦你能够将你的函数编译成DLL,你就可以从.NET中轻松调用它。正如我在之前链接到的另一篇文章中提到的,请记住导出函数时指定的调用约定。NET中的标准是stdcall
,而HaskellFFI
的(大多数)示例使用ccall
导出
到目前为止,我发现FFI可以导出的唯一限制是多态类型或未完全应用的类型。e、 g.除种类以外的任何内容(例如,您不能导出可能
,但可以导出可能是Int
)
我已经编写了一个工具,可以自动覆盖和导出示例中的任何函数。它还可以选择生成unsafe
C#代码,这使得它几乎可以“即插即用”。我之所以选择不安全代码,是因为它更容易处理指针,从而更容易对数据结构进行编组
最后,我将详细介绍该工具如何处理您的示例,以及我计划如何处理多态类型
- 高阶函数
假设我们将
Int
转换为CInt
则double的类型从
(Int -> Int) -> Int -> Int
进入
FunPtr (CInt -> CInt) -> CInt -> IO CInt
这些类型是为导出的包装函数(在本例中为double a
)而不是double
本身生成的。包装函数在原始函数的导出值和预期输入值之间映射。之所以需要IO,是因为构造FunPtr
不是纯粹的操作。需要记住的一点是,构造或取消引用
FunPtr
的唯一方法是静态创建导入,该导入指示GHC为此创建存根
foreign import stdcall "wrapper" mkFunPtr :: (Cint -> CInt) -> IO (FunPtr (CInt -> CInt))
foreign import stdcall "dynamic" dynFunPtr :: FunPtr (CInt -> CInt) -> CInt -> CInt
“wrapper”函数允许我们创建一个FunPtr
,而“dynamic”FunPtr
则允许我们尊重一个
在C#中,我们将输入声明为IntPtr
,然后使用Marshaller
helper函数创建一个我们可以调用的函数指针,或者使用反向函数从函数指针创建一个IntPtr
还要记住,作为参数传递给FunPtr的函数的调用约定必须与参数传递给的函数的调用约定相匹配。换句话说,将&foo
传递到bar
需要foo
和bar
具有相同的调用约定
- 用户数据类型
大小
和对齐方式
,以及如何向指针读取/写入类型的值。我部分用于此任务(因此文件中有C宏)
newtypes
或datatypes
只需一个构造函数就很容易了。由于在构造/销毁这些类型时只有一种可能的选择,因此这些类型将成为平面结构。具有多个构造函数的类型成为一个并集(在C#中,Layout
属性设置为Explicit
)。但是,我们还需要包含一个枚举来标识正在使用的构造
通常,数据类型Single
定义为
data Single = Single { sint :: Int
, schar :: Char
}
创建以下可存储的实例
instance Storable Single where
sizeOf _ = 8
alignment _ = #alignment Single_t
poke ptr (Single a1 a2) = do
a1x <- toNative a1 :: IO CInt
(#poke Single_t, sint) ptr a1x
a2x <- toNative a2 :: IO CWchar
(#poke Single_t, schar) ptr a2x
peek ptr = do
a1' <- (#peek Single_t, sint) ptr :: IO CInt
a2' <- (#peek Single_t, schar) ptr :: IO CWchar
x1 <- fromNative a1' :: IO Int
x2 <- fromNative a2' :: IO Char
return $ Single x1 x2
函数foo::Int->Single
将导出为foo::CInt->Ptr Single
而具有多个构造函数的数据类型
data Multi = Demi { mints :: [Int]
, mstring :: String
}
| Semi { semi :: [Single]
}
生成以下C代码:
enum ListMulti {cMultiDemi, cMultiSemi};
typedef struct Multi Multi_t;
typedef struct Demi Demi_t;
typedef struct Semi Semi_t;
struct Multi {
enum ListMulti tag;
union MultiUnion* elt;
} ;
struct Demi {
int* mints;
int mints_Size;
wchar_t* mstring;
} ;
struct Semi {
Single_t** semi;
int semi_Size;
} ;
union MultiUnion {
struct Demi var_Demi;
struct Semi var_Semi;
} ;
Storable
实例相对简单,应该遵循ea
typedef struct Single Single_t;
struct Single {
int sint;
wchar_t schar;
} ;
data Multi = Demi { mints :: [Int]
, mstring :: String
}
| Semi { semi :: [Single]
}
enum ListMulti {cMultiDemi, cMultiSemi};
typedef struct Multi Multi_t;
typedef struct Demi Demi_t;
typedef struct Semi Semi_t;
struct Multi {
enum ListMulti tag;
union MultiUnion* elt;
} ;
struct Demi {
int* mints;
int mints_Size;
wchar_t* mstring;
} ;
struct Semi {
Single_t** semi;
int semi_Size;
} ;
union MultiUnion {
struct Demi var_Demi;
struct Semi var_Semi;
} ;
instance Storable Int => Storable (Maybe Int) where