Haskell 中 FFI 调用的类型自动转换

2023-11-26

我定义了以下模块来帮助我导出 FFI 函数:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances #-}
module ExportFFI where

import Foreign
import Foreign.C


class FFI basic ffitype | basic -> ffitype where
    toFFI :: basic -> IO ffitype
    fromFFI :: ffitype -> IO basic
    freeFFI :: ffitype -> IO ()

instance FFI String CString where
    toFFI = newCString
    fromFFI = peekCString
    freeFFI = free

我正在努力处理函数实例。有人能帮我吗?


您可以利用涉及 FFI 的功能执行以下两件事: 1) 编组:这意味着将函数转换为可以通过 FFI 导出的类型。这是通过FunPtr。 2) 导出:这意味着为非 Haskell 代码创建一种调用 Haskell 函数的方法。

您的 FFI 类有助于编组,首先我创建了一些如何编组函数的示例实例。

这是未经测试的,但它可以编译,我希望它能工作。首先,让我们稍微改变一下类:

class FFI basic ffitype | basic -> ffitype, ffitype -> basic where
    toFFI :: basic -> IO ffitype
    fromFFI :: ffitype -> IO basic
    freeFFI :: ffitype -> IO ()

这表示,给定“basic”或“ffitype”类型,另一个是固定的[1]。这意味着不再可能将两个不同的值编组为同一类型,例如你不能再同时拥有两者

instance FFI Int CInt where

instance FFI Int32 CInt where

这样做的原因是因为freeFFI不能按照您定义的方式使用;无法仅从 ffitype 中确定选择哪个实例。或者,您可以将类型更改为freeFFI :: ffitype -> basic -> IO (), 或更好?)freeFFI :: ffitype -> IO basic。那么你就根本不需要fundeps了。

分配 FunPtr 的唯一方法是使用“foreign import”语句,该语句仅适用于完全实例化的类型。您还需要启用ForeignFunctionInterface扩大。结果是toFFI函数,它应该返回一个IO (FunPtr x),不能对函数类型进行多态。换句话说,你需要这个:

foreign import ccall "wrapper"
  mkIntFn :: (Int32 -> Int32) -> IO (FunPtr (Int32 -> Int32))

foreign import ccall "dynamic"
  dynIntFn :: FunPtr (Int32 -> Int32) -> (Int32 -> Int32)

instance FFI (Int32 -> Int32) (FunPtr (Int32 -> Int32)) where
    toFFI = mkIntFn
    fromFFI = return . dynIntFn
    freeFFI = freeHaskellFunPtr

对于您想要编组的每种不同的函数类型。您还需要FlexibleInstances此实例的扩展。 FFI 施加了一些限制:每种类型都必须是可编组外部类型,并且函数返回类型必须是可编组外部类型或返回可编组外部类型的 IO 操作。

对于不可编组类型(例如字符串),您需要稍微复杂一些的东西。首先,由于编组发生在 IO 中,因此您只能编组导致 IO 操作的函数。 如果你想编组纯函数,例如(String -> String),您需要将它们提升为 (String -> IO String) 的形式。[2]让我们定义两个助手:

wrapFn :: (FFI a ca, FFI b cb) => (a -> IO b) -> (ca -> IO cb)
wrapFn fn = fromFFI >=> fn >=> toFFI

unwrapFn :: (FFI a ca, FFI b cb) => (ca -> IO cb) -> (a -> IO b)
unwrapFn fn a = bracket (toFFI a) freeFFI (fn >=> fromFFI)

这些将函数的类型转换为适当的编组值,例如wrapStrFn :: (String -> IO String) -> (CString -> IO CString); wrapStrFn = wrapFn。注意unwrapFn使用“Control.Exception.bracket”来确保在发生异常时释放资源。忽略这个你可以写unwrapFn fn = toFFI >=> fn >=> fromFFI;看看与wrapFn 的相似之处。

现在我们有了这些助手,我们可以开始编写实例了:

foreign import ccall "wrapper"
  mkStrFn :: (CString -> IO CString) -> IO (FunPtr (CString -> IO CString))

foreign import ccall "dynamic"
  dynStrFn :: FunPtr (CString -> IO CString) -> (CString -> IO CString)

instance FFI (String -> IO String) (FunPtr (CString -> IO CString)) where
    toFFI = mkStrFn . wrapFn
    fromFFI = return . unwrapFn . dynStrFn
    freeFFI = freeHaskellFunPtr

和以前一样,不可能使这些函数具有多态性,这导致了我对这个系统最大的保留。这是很大的开销,因为您需要为每种类型的函数创建单独的包装器和实例。除非您要进行大量的功能编组,否则我会严重怀疑这是否值得付出努力。

这就是编组函数的方式,但是如果您想让它们可用于调用代码怎么办?这个另一个过程是出口该功能,我们已经开发了大部分必需的功能。

导出的函数必须具有可编组类型,就像FunPtrs。我们可以简单地重复使用wrapFn去做这个。要导出一些函数,您需要做的就是将它们包装起来wrapFn并导出包装版本:

f1 :: Int -> Int
f1 = (+2)

f2 :: String -> String
f2 = reverse

f3 :: String -> IO Int
f3 = return . length

foreign export ccall f1Wrapped :: CInt -> IO CInt
f1Wrapped = wrapFn (return . f1)

foreign export ccall f2Wrapped :: CString -> IO CString
f2Wrapped = wrapFn (return . f2)

foreign export ccall f3Wrapped :: CString -> IO CInt
f3Wrapped = wrapFn f3

不幸的是,这种设置仅适用于单参数函数。为了支持所有功能,让我们再创建一个类:

class ExportFunction a b where
  exportFunction :: a -> b

instance (FFI a ca, FFI b cb) => ExportFunction (a->b) (ca -> IO cb) where
  exportFunction fn = (wrapFn (return . fn))

instance (FFI a ca, FFI b cb, FFI d cd) => ExportFunction (a->b->d) (ca->cb->IO cd) where
  exportFunction fn = \ca cb -> do
    a <- fromFFI ca
    b <- fromFFI cb
    toFFI $ fn a b

现在我们可以使用exportFunction对于具有 1 个和 2 个参数的函数:

f4 :: Int -> Int -> Int
f4 = (+)

f4Wrapped :: CInt -> CInt -> IO CInt
f4Wrapped = exportFunction f4

foreign export ccall f4Wrapped :: CInt -> CInt -> IO CInt

f3Wrapped2 = :: CString -> IO CInt
f3Wrapped2 = exportFunction f3

foreign export ccall f3Wrapped2 :: CString -> IO CInt
f3Wrapped2 = exportFunction f3

现在你只需要编写更多实例ExportFunction自动将任何函数转换为适当的类型以进行导出。我认为这是在不使用某种类型的预处理器或 unsafePerformIO 的情况下可以做的最好的事情。

[1] 从技术上讲,我认为不需要“basic -> ffitype”fundep,因此您可以删除它以使一种基本类型能够映射到多个 ffitype。这样做的原因之一是将所有大小的整数映射到整数,尽管toFFI实施将会有损。

[2] 稍微简化。你可以编组一个函数String -> StringFFI 类型CString -> IO CString。但现在你无法转换CString -> IO CString函数返回到String -> String因为IO的返回类型。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Haskell 中 FFI 调用的类型自动转换 的相关文章

  • Haskell 中动态规划的高效表

    我已经编码了0 1背包问题 http en wikipedia org wiki Knapsack problem 0 1 knapsack problem在哈斯克尔 我对迄今为止所取得的懒惰和普遍性水平感到相当自豪 我首先提供用于创建和处
  • 当单态限制打开*时,如何解决歧义问题?

    因此 在学习 Haskell 时 我很快就遇到了可怕的单态限制 在 ghci 中 Prelude gt let f print show Prelude gt f 5
  • 绑定变量时 Haskell 中的无限循环

    下面的 Haskell 代码不会终止 有人可以解释一下为什么吗 谢谢 f let x 10 in let x x x in x 我认为解释器首先绑定 x 10 然后将 x x 计算为 100 并绑定 x 100 环境变为 x 100 那么整
  • 在 Haskell 中等待然后检测按键的简单方法是什么?

    我对 Haskell 还很陌生 所以我正在寻找一种简单的方法来检测按键 而不是使用getLine 如果有人知道任何库 或者知道一些这样做的技巧 那就太好了 如果有更好的地方可以问这个问题 请直接告诉我 我将不胜感激 如果您不想阻止 可以使用
  • 在 Haskell 中为自定义数据类型创建 Read 类型类的实例

    我有一个自定义数据类型Foo Foo a Int b Int 我正在尝试使 Foo 成为 read 的自定义实例 我已经有一个功能了bar String gt Foo我尝试这样做 instance Read Foo a b where re
  • 我们不应该使用单子绑定来使用循环写下 mfix 的情况

    我一直在尝试写mfix向下使用Control Arrow loop https hackage haskell org package base 4 14 0 0 docs src Control Arrow html loop 我想出了不
  • 计算/获取分层数据的“级别”

    好吧 我真的不知道这是否是正确的标题 但我不知道如何称呼它 我的问题是关于我的作业 我现在已经工作了几个小时 主题是 函数式数据结构 我有点陷入困境 我不知道如何继续 所以我需要编写一个具有以下签名的函数 data Heap e t Hea
  • 使用 Haskell 的欧拉项目 #1

    import Data Set euler Int euler sum x x lt nums where nums Data Set toList Data Set union Data Set fromList 3 6 999 Data
  • 副作用是纯函数中找不到的一切吗?

    可以肯定地说 以下二分法成立 每个给定的函数是 要么纯粹 或有副作用 如果是这样 函数的 副作用就是纯函数中找不到的任何东西 这很大程度上取决于您选择的定义 可以公平地说 函数是pure or impure 纯函数始终返回相同的结果并且不会
  • 不同 hs 文件中的函数分离时堆栈空间溢出

    我有一个巨大的 haskell 文件 它编译和运行没有任何问题 我想将一些函数和类型定义放在通用 hs 文件中的单独模块中 然后将其导入我的主模块中 虽然主程序编译时没有任何错误 它还编译导入的模块 但当我尝试运行它时 出现堆栈空间溢出 I
  • Haskell printf 转字符串

    Haskell 中有等效的 sprintf 吗 我需要将双精度值转换并格式化为字符串 有没有其他方法而不使用printf什么样的功能 主要问题是要避免 Prelude gt putStrLn myDoubleVal 1 7944444444
  • 为什么haskell中的递归列表这么慢?

    我对 Haskell 很陌生 我在 Haskell 中定义了一个函数 febs Integral a gt a gt a febs n n lt 0 0 n 1 1 n 2 1 otherwise febs n 1 febs n 2 但是
  • 如何将只缓存某些内容的字段添加到ADT?

    我经常需要向 ADT 添加字段 仅记住一些冗余信息 但我还没有完全弄清楚如何又好又高效地做到这一点 说明问题的最好方法是举个例子 假设我们正在使用无类型 lambda 项 type VSym String data Lambda Var V
  • Haskell 中的相互递归求值器

    Update 我已经添加一个答案 https stackoverflow com questions 3524485 mutually recursive evaluator in haskell 4504200 4504200这描述了我的
  • 在 haskell 中处理 IO 与纯代码

    我正在编写一个shell脚本 我在haskell中的第一个非示例 它应该列出一个目录 获取每个文件大小 进行一些字符串操作 纯代码 然后重命名一些文件 我不确定我做错了什么 所以有两个问题 我应该如何安排这样的程序中的代码 我有一个具体问题
  • 存在函数依赖关系时类型推断如何工作

    考虑下面的代码 LANGUAGE MultiParamTypeClasses FlexibleInstances FunctionalDependencies UndecidableInstances FlexibleContexts cl
  • 为什么 exceptT 没有 MonadMask 实例?

    爱德华 克梅特例外情况图书馆不提供单子掩码 https www stackage org haddock lts 7 18 exceptions 0 8 3 Control Monad Catch html t MonadMask实例为Ex
  • 有什么方法可以在 do / while / let 块中打印出变量的类型吗?

    有没有办法打印出嵌套变量的推断类型ghci 考虑代码 let f g where g x Int x 那么 最好查询一下类型g e g t f g会打印出Int gt Int 您可以通过给出适当的错误类型注释并检查错误消息来诱骗此信息 Ma
  • Haskell 排列库函数 - 请澄清一下?

    这是代码permutationsHaskell 中的函数Data List module permutations a gt a permutations xs0 xs0 perms xs0 where perms perms t ts i
  • 类型级编程有哪些示例? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我不明白 类型级编程 是什么意思 也无法使用Google找到合适的解释 有人可以提供一个演示类型级编程的示例吗 范式的解释和 或定义将

随机推荐

  • 管理 TPL 队列

    我有一项运行各种服务器扫描的服务 所涉及的网络可能非常庞大 数十万个网络节点 该软件的当前版本使用的是我们设计的队列 线程架构 该架构可以工作 但效率不高 尤其是因为作业可能会产生处理不好的子项 V2 即将推出 我正在考虑使用 TPL 看起
  • Java 标签不规则(可能是错误?)

    如果我们看一下Java标准 14 7 我们看到语句可能有标签前缀 例如 标签声明 标识符 声明 理论上 标签应该能够标记任何后续语句 因此 例如 以下内容将相应编译 public class Test public static void
  • OpenCV unproject 2D 指向具有已知深度“Z”的 3D

    问题陈述 我正在尝试将 2D 点重新投影到其原始 3D 坐标 假设我知道每个点的距离 继OpenCV 文档 我设法让它以零失真的方式工作 然而 当存在扭曲时 结果就不正确 目前的方法 因此 我们的想法是反转以下内容 分为以下内容 By 使用
  • 使用 jQuery 的“是”或“否”确认框

    我想要使 用 jQuery 发出 是 否 警报 而不是 确定 取消 按钮 jQuery alerts okButton Yes jQuery alerts cancelButton No jConfirm Are you sure func
  • Qt XML 中属性的顺序不正确

    我有以下代码 element clear element setTagName accountpoint element setAttribute code QString ID CONST serial element setAttrib
  • 自动构建 NuGet 包,包括引用的依赖项

    我想要运行本地 内部 NuGet 存储库 我想我已经弄清楚如何 重用 现有的 NuGet 包 方法是将它们包含在使用 NuGet 的虚拟项目中并扫描包文件以获取我的本地缓存 nupkg files but 如何创建 nuget 包 nupk
  • 将 2D 数组转换为 3D numpy 数组

    我创建了一个 numpy 数组 数组的每个元素都包含相同形状的数组 9 5 我想要的是一个 3D 数组 我尝试过使用 np stack data list map lambda x getKmers x 9 data getKmers cr
  • RabbitMQ / AMQP 中的消息组

    ActiveMQ JMS 有一个内置机制 可确保在使用竞争消费者模式时 共享公共标头 即 JMSXGroupID 标头 的消息始终由队列的同一使用者使用 队列的消费者完全不知道实际的标头值 因为具有公共标头的消息的保证是在服务器端而不是消费
  • 估计未定义表面的梯度

    我想估计一个梯度 斜率和坡向 不明确的表面 即函数未知 为了测试我的方法 这里是测试数据 require raster require rasterVis set seed 123 x lt runif 100 min 0 max 1 y
  • 为什么 std::vector::insert 使插入点之后的所有迭代器无效

    When insert ing 成std vectorC 标准确保插入点之前的所有迭代器都保持有效 只要capacity未耗尽 参见 23 2 4 3 1 或std vector 迭代器失效 不允许插入点之后的迭代器保持有效 如果容量未耗尽
  • C# 4.0 中的类属性/字段可以是匿名类型吗?

    As in public class MyClass private static var MyProp new item1 a item2 b 注意 上面的代码既不能编译也不能工作 var 不能在那里使用 它只是为了表明我的观点 Upda
  • iOS 7 中锁屏播放器的搜索栏问题

    如果我在 iOS 7 中通过 iPhone 的本机音乐播放器播放音乐并在锁定屏幕上访问它 那么我可以拖动播放器的搜索栏 即用户可以与其交互 截屏 但是 如果我从支持后台音频播放的应用程序播放音乐 则播放器的搜索栏用户交互将在锁定屏幕上禁用
  • 如何从Linux内核访问用户空间内存?

    我知道copy to user copy from user get user put user函数就是为了这个目的 我的问题是 给定一个用户空间地址 指针 我如何从内核访问该地址指向的数据 我可以想象 首先我必须确保包含的页面应该位于物理
  • 空手道-需要帮助来断言日期范围的一维数组

    我试图断言一维数组内的值 我尝试过使用 match 但看起来无法断言日期范围 下面是对象数组 2019 04 24T17 41 28 2019 04 24T17 41 27 975 2019 04 24T17 41 27 954 2019
  • 在服务器上使用socket.io和nodejs,并以apache作为反向代理

    我尝试将 Node js 与 Socket IO 结合使用来促进浏览器和客户端之间的消息传递 如下导游 但是 我必须在 Apache 后面设置 Node 反向代理 因此 我使用 example com nodejs 代替 example c
  • Google Appengine 和 rx-Java?

    rxJava 库与 Google Appengine 兼容吗 如果可以的话有什么限制吗 我发现的唯一信息是提到 grepcode 上的 部分支持 http grepcode com snapshot repo1 maven org mave
  • Android RecyclerView 选择第一个 Item

    我正在使用 RecyclerView 来实现 NavigationDrawer 我让点击事件正常工作 但我不知道如何在应用程序启动时选择第一个项目 然后即使未显示抽屉 也可以保持所选项目高亮显示 我所能找到的只是 RecyclerView
  • mysql CREATE VIEW 无法从 mysql_query 工作

    我有一个在 mysql 数据库中创建 VIEW 的代码 该代码在我的本地服务器上运行良好 它可以正常创建和裁剪视图 但在我的在线服务器上它给出了错误 CREATE VIEW command denied to user 对于在线数据库 我在
  • C#:在并行线程中设置 HttpContext.Current 有什么问题吗?

    我正在使用一个依赖于 HttpContext Current 的库 该库是 Facebook C SDK 但我的问题也应该适用于其他场景 我想从并行线程内部使用这个库 但是 HttpContext Current 在并行线程中不可用 因此我
  • Haskell 中 FFI 调用的类型自动转换

    我定义了以下模块来帮助我导出 FFI 函数 LANGUAGE MultiParamTypeClasses FunctionalDependencies TypeSynonymInstances module ExportFFI where