假设您具有提供C结构的C API
typedef struct A {
int i;
float f;
} A;
和填充它的函数:
void getA(A* a);
例如,这可能是从C API的内部获取某些信息的吸气剂。
在Haskell中,C结构将被镜像
data A = A {
i :: Int,
f :: Float
}
Storable
实例是
instance Storable A where
sizeOf _ = {#sizeof A #}
alignment _ = {#alignof A #}
peek p = ...
poke p x = ...
窥视和戳戳与往常一样,由c2hs处理{#get...#}
和{#set #}
编译指示。
Haskell函数getA :: IO A
应该类似于
{#fun unsafe getA as getA {alloca- `A' peek*} -> `()'#}
除了这不起作用,因为c2hs创建了此绑定:
foreign import ccall unsafe "include/A.h getA"
_getA'_ :: Ptr () -> IO ()
它的第一个参数为Ptr ()
。可以通过以下方式来解决
{#fun unsafe getA as getA {allocaA- `A' peekA*} -> `()'#}
peekA :: Ptr () -> IO A
peekA = peek . castPtr
allocaA :: (Ptr () -> IO a) -> IO a
allocaA f = alloca $ \(p :: Ptr A) -> f (castPtr p)
allocaA
很重要,因为如果使用了A
,它可以确保分配()
的内存而不是仅分配alloca
的内存。
尽管这有效,但它有些乏味,并且如果您忘记写allocaXYZ
而不是仅仅写alloca
,则可以保证存在段错误。 (我刚刚发现花了很多时间来追踪这样的错误。)
我希望找到会产生
的{#fun...#}
咒语
foreign import ccall unsafe "include/A.h getA"
_getA'_ :: Ptr A -> IO ()
,其他所有内容都会自然地跟随(请注意,Ptr A
而不是Ptr ()
)。但据我所知,只有{allocXYZ- 'XYZ' peekXYZ*}
条路线。
所以问题是:可以使用c2hs
更好地做到这一点吗?
答案 0 :(得分:0)
您使用{#pointer ...#}
挂钩吗?由于您不会像对待不透明指针那样对待A(即i
和f
可以正常访问,并且Storable
实例是明确编写的),因此,您需要使用箭头形式:
{#pointer *A as APtr -> A#}
到那时,您仍然必须使用alloca
/ peek
函数来编组A
,但是您的第一个理想的{#fun ...#}
钩应该按编写的方式工作。 (您最终会忽略APtr
类型;它会在生成的代码中显示,但在*.chs
文件中不是必需的。)
还要注意,您需要将该指针定义添加到每个使用的文件A
中;即使您从一个主文件中导出APtr
,您仍然需要在使用它的所有位置添加{#pointer *A as APtr -> A nocode#}
。