Eon*_*nil 22 c syntax haskell ffi
首先,我是Haskell的初学者.
我正计划将Haskell集成到C中用于实时游戏.Haskell做逻辑,C做渲染.要做到这一点,我必须为每个滴答(每秒至少30次)从彼此传递巨大的复杂结构数据(游戏状态).所以传递的数据应该是轻量级的.该状态数据可以放在存储器上的顺序空间上.Haskell和C部分都应该自由地访问各州的每个区域.
在最好的情况下,传递数据的成本可以是复制指向内存的指针.在最坏的情况下,通过转换复制整个数据.
我正在阅读Haskell的FFI(http://www.haskell.org/haskellwiki/FFICookBook#Working_with_structs)Haskell代码看起来明确指定了内存布局.
我有几个问题.
#{alignment foo}
什么?我在哪里可以找到关于此的文件?*PS显式内存布局功能,我说只是C#的[StructLayout]属性.这明确指定了内存中的位置和大小. http://www.developerfusion.com/article/84519/mastering-structs-in-c/
我不确定Haskell是否匹配语言构造与C struct的字段匹配.
Joh*_*n L 24
我强烈建议使用预处理器.我喜欢c2hs,但是hsc2hs很常见,因为它包含在ghc中.Greencard似乎被放弃了.
回答你的问题:
1)是的,通过可存储实例的定义.使用Storable是通过FFI传递数据的唯一安全机制.Storable实例定义了如何在Haskell类型和原始内存(Haskell Ptr,ForeignPtr或StablePtr或C指针)之间编组数据.这是一个例子:
data PlateC = PlateC {
numX :: Int,
numY :: Int,
v1 :: Double,
v2 :: Double } deriving (Eq, Show)
instance Storable PlateC where
alignment _ = alignment (undefined :: CDouble)
sizeOf _ = {#sizeof PlateC#}
peek p =
PlateC <$> fmap fI ({#get PlateC.numX #} p)
<*> fmap fI ({#get PlateC.numY #} p)
<*> fmap realToFrac ({#get PlateC.v1 #} p)
<*> fmap realToFrac ({#get PlateC.v2 #} p)
poke p (PlateC xv yv v1v v2v) = do
{#set PlateC.numX #} p (fI xv)
{#set PlateC.numY #} p (fI yv)
{#set PlateC.v1 #} p (realToFrac v1v)
{#set PlateC.v2 #} p (realToFrac v2v)
Run Code Online (Sandbox Code Playgroud)
该{# ... #}
片段是c2hs代码. fI
是fromIntegral
.get和set片段中的值从包含的头引用以下结构,而不是同名的Haskell类型:
struct PlateCTag ;
typedef struct PlateCTag {
int numX;
int numY;
double v1;
double v2;
} PlateC ;
Run Code Online (Sandbox Code Playgroud)
c2hs将其转换为以下普通Haskell:
instance Storable PlateC where
alignment _ = alignment (undefined :: CDouble)
sizeOf _ = 24
peek p =
PlateC <$> fmap fI ((\ptr -> do {peekByteOff ptr 0 ::IO CInt}) p)
<*> fmap fI ((\ptr -> do {peekByteOff ptr 4 ::IO CInt}) p)
<*> fmap realToFrac ((\ptr -> do {peekByteOff ptr 8 ::IO CDouble}) p)
<*> fmap realToFrac ((\ptr -> do {peekByteOff ptr 16 ::IO CDouble}) p)
poke p (PlateC xv yv v1v v2v) = do
(\ptr val -> do {pokeByteOff ptr 0 (val::CInt)}) p (fI xv)
(\ptr val -> do {pokeByteOff ptr 4 (val::CInt)}) p (fI yv)
(\ptr val -> do {pokeByteOff ptr 8 (val::CDouble)}) p (realToFrac v1v)
(\ptr val -> do {pokeByteOff ptr 16 (val::CDouble)}) p (realToFrac v2v)
Run Code Online (Sandbox Code Playgroud)
偏移当然是与体系结构相关的,因此使用预处理器可以编写可移植代码.
您可以通过为数据类型(new
,malloc
等)分配空间并将数据分配poke
到Ptr(或ForeignPtr)来使用它.
2)这是真正的内存布局.
3)使用peek
/ 读取/写入会受到处罚poke
.如果你有很多数据,最好只转换你需要的东西,例如只从C数组中读取一个元素,而不是将整个数组编组到Haskell列表中.
4)语法取决于您选择的预处理器. c2hs docs. hsc2hs文档.令人困惑的是,hsc2hs使用语法#stuff
或#{stuff}
,而c2hs使用{#stuff #}
.
5)@sclv的建议也是我要做的.编写一个可存储的实例并保留指向数据的指针.您可以编写C函数来完成所有工作并通过FFI调用它们,或者(不太好)使用peek和poke编写低级Haskell来操作所需的数据部分.整齐地来回编组(即调用peek
或poke
整个数据结构)将是昂贵的,但如果你只是通过指针,成本将是最小的.
通过FFI调用导入的函数会受到重大惩罚,除非它们被标记为"不安全".声明导入"不安全"意味着该函数不应回调到Haskell或未定义的行为结果.如果你正在使用并发或并行,它也意味着相同功能(即CPU)上的所有Haskell线程都将阻塞,直到调用返回,因此它应该相当快地返回.如果这些条件可以接受,则"不安全"呼叫相对较快.
Hackage上有很多软件包来处理这类事情.我可以推荐hsndfile和hCsound作为c2hs的良好实践.如果你看一下你熟悉的小型C库的绑定可能会更容易.
即使你可以获得严格的无盒装Haskell结构的确定性内存布局,但是没有任何保证,这是一个非常糟糕的主意.
如果您愿意接受转换,可以使用Storeable:http://www.haskell.org/ghc/docs/6.12.3/html/libraries/base-4.2.0.2/Foreign-Storable.html
我要做的是构造C结构,然后使用FFI构造直接在它们上运行的Haskell函数,而不是试图为它们生成Haskell"等价物".
或者,您可以决定只需要将选择的信息传递给C - 而不是整个游戏状态,而只是关于世界上哪些对象的一些信息,以及您的实际信息如何吸引他们只生活在等式的C面.然后,您在Haskell中执行所有逻辑,在本机Haskell结构上运行,并且仅向C世界投射C实际需要呈现的微小数据子集.
编辑:我应该补充说,矩阵和其他常见的c结构已经有很好的库/绑定,可以保持c侧的繁重.
归档时间: |
|
查看次数: |
2822 次 |
最近记录: |