Haskell C FFI:访问静态数据结构

16
我有一个关于Haskell C FFI的问题,具体是关于如何访问由C库导出的静态数据结构。
我要包装的C库有像下面的FOO_GEORGE这样的静态数据结构,以以下方式导出:
static struct foo_struct foo_table[] = { /* list of foo structs */ }
typedef struct foo_struct *foo_t;
foo_t FOO_GEORGE = &foo_table[0];
foo_t FOO_HARRY  = &foo_table[1];
foo_t FOO_SUSAN  = &foo_table[2];
/* ... */

我在我的Haskell库中需要的值是foo_struct的地址(&foo_table[n]),也就是FOO_GEORGE的内容,通常情况下会将其放入不透明的新类型包装器中(构造函数不会从库中导出,只导出类型):

newtype Foo = Foo { getFoo :: (Ptr Foo) }

这是我现在正在做的事情:

foreign import ccall "&FOO_GEORGE" fooGeorgeHandle :: Ptr (Ptr Foo)
FooGeorge = Foo . unsafeDupablePerformIO . peek $ fooGeorgeHandle

我认为这是一个适当使用 unsafePerformIO 的例子,因为 C API 和实现表明这种使用 peek 是纯净的且没有副作用。此外,我相信我不需要采取文档中列出的符号开头的任何预防措施 ({-# NOINLINE foo #-} 开始)。

我的总问题是:我做得对吗?上面的分析是否正确?有更好或更可取的方法吗?如果 foreign import 子句允许我进行指针引用,那就太好了,但似乎不行;我有遗漏吗?可以说这将是一个糟糕的特性,因为如果指针无效会导致段错误,但这同样也适用于我必须使用的 peek,所以本质上并无区别。

谢谢!


1
我之前不知道CApiFFI(或者其他文档中提到的内容),但是看起来很有前途。我会试一下并回报结果。谢谢! - Richard E. Silverman
1
我想我应该回答,但我从未使用过 CApiFFI,所以我不确定它是否有效。 - John L
1
请考虑回答您自己的问题,而不是编辑问题。 - askmish
1
你有没有机会为ghci编写解决方法? - Edward Kmett
感谢您的评论。我会在这个周末将其重写为答案,并添加ghci解决方法。 - Richard E. Silverman
显示剩余4条评论
1个回答

3

John L.建议使用CApiFFI扩展,它正好符合我的要求:允许您导入值而不是位置。现在:

{-# LANGUAGE CApiFFI #-}
newtype {-# CTYPE "foo.h" "struct foo_struct" #-} Foo = Foo { getFoo :: (Ptr Foo) }
foreign import capi "foo.h value FOO_GEORGE" fooGeorgePtr :: Ptr a
fooGeorge = Foo fooGeorgePtr

另一个优点是,无论FOO_GEORGE是C变量还是预处理器宏,这都适用。我正在使用的C API同时使用两者,而我链接到的同一API的不同实现会以不同的方式进行,因此独立于此非常好。
然而,有一个绊脚石:CApiFFI无法与ghci一起使用!问题已经被知道,并且直到GHC 8.0.1才会修复(当我第一次写这篇文章时,它应该在7.10中完成,然后被推迟了)。我有一个非常hacky的解决方法。CApiFFI通过即时生成C库来工作,编译和链接Haskell程序。完成后删除库;ghci的问题似乎是.so文件在ghci需要链接时已经不存在了。在它被删除之前,我只需在编译期间获取.c文件,然后自己编译它并告诉ghci加载它。由于我很少更改程序的那部分,所以对我来说效果很好。
我的捕获临时.c文件的方法是在Emacscompilation-mode中启动ghci,并使用(setq compilation-auto-jump-to-first-error t)。 Emacs看到错误并在GHC删除它之前将.c文件加载到缓冲区中 - 当我看到它时,该文件已经不存在了,但我已经在缓冲区中获得了内容。
更新: ghci-fobject-code Foo可行,但只能查看从模块导出的名称。

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