基于复杂的Haskell类型生成C结构体

11

我正在尝试在我的C代码中使用Haskell库。我要使用的Haskell函数具有类型String -> IO [Reference],其中Reference是一个相当复杂的结构(详见此处)。

根据阅读各种文档的经验,似乎我需要将这种类型设为Storable的实例,并且还必须在我的C代码中定义类似的结构才能访问它。对于这样一种复杂的类型来说,这似乎需要做很多非常重复的工作。是否有一种自动化的方式?如何做到这一点?


2
这并不回答你的问题,但是hsc2hs可以帮助你为具有相应C结构的类型编写Storable实例。 - gspr
是的,我需要相反的东西 - 根据 Haskell 类型编写 C 结构体的工具。 - Jakub Hampl
2
我认为自动化的难点在于如何决定将Haskell数据类型完全通用地转换成C。看起来你的类型很大,但并不是非常复杂——非递归且由具有命名字段的单构造器类型或枚举类型构建而成。将其转换为C将是完全直接的。 - C. A. McCann
2个回答

8
这取决于您的实际使用情况,但是... 将Reference作为不透明类型(通过Foreign.StablePtr)导出可能更容易,然后导出获取器函数以访问各个字段。
如果需要更多详细信息,请告诉我,我会扩展答案。

看起来不错。我在想如何返回一个StablePtr列表? - Jakub Hampl
2
@JakubHampl 你可以尝试使用Foreign.Marshal.Array.newArray或类似的东西。 - Yuras

4
我写了一个小工具(使用Template Haskell),它可以自动将由原始类型(Int,Float,Double,Char,Bool)、可编组类型的列表和由可编组类型组成的结构体组成的任何数据类型转换为相应的C类型。
  • 原始类型变为它们的C对应项:Int -> int,Float -> float。Bool 变成 int。
  • "结构体"(data S = S ...)变成指向带有Haskell "结构体"的编组成员的结构体的指针。
  • 数组([S])变成指向包含指向该类型的指针数组和一个int的结构体的指针,告诉其中有多少元素。

所以这个:

data Test = Test [MyStruct] Int
data MyStruct = MyStruct Int

在C语言中,它的写法如下:

struct MyStruct {
  int x;
}

struct ArrayStruct {
  MyStruct** array;
  int count;
}

struct Test {
  ArrayStruct* arr_str;
  int y;
}

这里有一个工具: https://github.com/food2games/fieldmarshal (它也有C#部分,但你需要HsFieldMarshal。)它由两个文件组成,你只需将它们复制到你的代码中即可。使用方法:
$(makeStorable ''YourType)

请注意,它不会自动为子类型执行可存储代码,因此如果您有以下内容:
data Type1 = Type1 Int Float
data Type2 = Type2 Int Type1

那么您需要为每个数据类型生成可存储实例:

$(makeStorable ''Type1)
$(makeStorable ''Type2)

还需注意,您必须在生成Storable实例之前先声明数据类型(这是由于TH引起的)。 因此,以下代码将无法正常工作:

$(makeStorable ''Wrong)
data Wrong = Wrong Int

这绝对不是万无一失的,对于简单应用来说已经足够了,但是如果你要处理更复杂的代码,事情很容易出错。


看起来很酷,但是在使用它时我遇到了一些错误。首先,支持C字符串会非常有用。其次,在生成RefType时,我得到了FieldMarshal.hs:(172,1)-(176,61): Non-exhaustive patterns in function makeDataType的错误提示。 - Jakub Hampl
来自@nulloid(他无法在此帖子上发表评论,因此我发布了他的评论):是的,那是计划中的,但我遇到了内存管理问题。此外,我目前不需要该功能,所以我没有投入太多精力。 - eboix
还有来自@nulloid的内容(太长了,一个评论放不下): “其次,当生成RefType时,在makeDataType函数中出现了FieldMarshal.hs:(172,1)-(176,61):非穷尽模式。”这可能是因为FieldMarshal不支持具有多个构造函数的类型。(我认为可以使用C联合实现...)抱歉,我应该更仔细地阅读您的问题。同时,感谢您的反馈。 - eboix
@nulloid的最后一段评论:(不幸的是,我没有时间积极地开发我不需要的功能。但是,如果有人想要理解和修改代码,我可以提供帮助。但是,如果您不需要广泛创建包装器(我做了),那么您可能只需手动编写一个marshaller。) - eboix
似乎网址https://github.com/food2games/fieldmarshal已经失效了。有任何关于他们是谁或者去了哪里的想法吗? - Jeff Burdges

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接