我有一个关于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];
/* ... */
Run Code Online (Sandbox Code Playgroud)
我在Haskell库中需要的值是foo_struct(&foo_table[n])的地址,即内容FOO_GEORGE,以通常的方式放入不透明的newtype包装器中(构造函数不是从库中导出的,只是类型):
newtype Foo = Foo { getFoo :: (Ptr Foo) }
Run Code Online (Sandbox Code Playgroud)
这就是我现在正在做的事情:
foreign import ccall "&FOO_GEORGE" fooGeorgeHandle :: Ptr (Ptr Foo)
FooGeorge = Foo . unsafeDupablePerformIO . peek $ fooGeorgeHandle
Run Code Online (Sandbox Code Playgroud)
我认为这是一个合适的用途unsafePerformIO,因为C API和实现说这种用法peek是纯粹的并且没有副作用.另外,我认为我不需要采取文档中的要点(从一开始 {-# NOINLINE foo #-})中列出的任何预防措施.
我的整体问题是:我这样做了吗?上面的分析是否正确?有没有更好或更好的方法来做到这一点?如果该foreign import条款允许我执行指针引用,那将是不错的,但它似乎不是; 我错过了什么吗?有人可能会说,这将是一个糟糕的功能,因为如果指针是坏的,它可能会出现段错误 - 但是,peek我必须使用相同的情况,因此它也是如此.
谢谢!
约翰·L.建议CApiFFI扩展,它正是我想要的:允许您导入值而不是位置。现在:
{-# LANGUAGE CApiFFI #-}\nnewtype {-# CTYPE "foo.h" "struct foo_struct" #-} Foo = Foo { getFoo :: (Ptr Foo) }\nforeign import capi "foo.h value FOO_GEORGE" fooGeorgePtr :: Ptr a\nfooGeorge = Foo fooGeorgePtr\nRun Code Online (Sandbox Code Playgroud)\n\n另一个优点是,无论是否FOO_GEORGE是 C 变量还是预处理器宏,它都有效。I\xe2\x80\x99m 使用的 C API 两者都使用,并且我链接的同一 API 的不同实现以不同的方式执行,因此独立于它是很棒的。
然而,有一个绊脚石:xe2x80x99CApiFFI不能与ghci一起工作!这个问题是已知的,\xe2\x80\x99t 预计会在 GHC 8.0.1 之前得到修复(当我第一次写这篇文章时,它应该在 7.10 上发布,然后被推迟)。我有一个非常hacky的解决方法。CApiFFI其工作原理是动态生成一个 C 库,针对它编译并链接 Haskell 程序。完成后它会删除库;ghci问题似乎是ghci需要链接时 .so 文件已经消失。我只是在编译期间抓取.c文件,然后将其删除,然后自己编译并告诉ghci加载它。由于我不经常更改程序的这一部分,因此它对我来说效果很好。
我捕获临时.c文件的方法是在 Emacs 中启动ghcicompilation-mode,然后使用(setq compilation-auto-jump-to-first-error t). Emacs 看到错误并在 GHC 开始删除它之前将.c文件加载到缓冲区中 \xe2\x80\x94 当我看到它时,文件已经消失了,但是我 \xe2\x80\x99 已经将内容保存在缓冲。
更新: ghci -fobject-code Foo可以,但只能看到从模块导出的名称。
| 归档时间: |
|
| 查看次数: |
406 次 |
| 最近记录: |