【问题标题】:How to Call C++ Setters & Getters from Haskell如何从 Haskell 调用 C++ Setter 和 Getter
【发布时间】:2019-05-30 05:36:20
【问题描述】:

我知道如何从 Haskell 调用纯 C++ 函数,但想知道如何让 GHC 接受有副作用的函数。

我希望 Haskell 拥有对 C++ 链表的只读访问权限,以及对 C++ 变量的独占写入访问权限。例如:

class node {
    // Some other class makes a linked list out of these
    int x;
    node* previous;
    node* next;
}

class myHaskellInterface {
    node* presentLocation; // C++ decides what is regarded as current location

    int getVal() {
        // Haskell calls this to get information from C++
        return presentLocation->x;
    }

    int haskellResults; // Store Haskell output here

    void setVal(int x) {
        // Haskell calls this to pass information back
        haskellResults = x;
    }

};

尽管 getVal() 没有副作用,但它是一个明显有副作用的类的一部分,所以我不清楚是否需要偷偷摸摸的技巧才能让 GHC 接受它。

setVal(int) 明明有副作用,那怎么让 GHC 不在乎呢?

【问题讨论】:

  • C 符号的 IO 类型的外部导入是否不是您想要的?您将在 cxx 文件中声明 C extern 并在 Haskell 中拥有 foreign import ccall "externed_getVal" getVal :: CxxObj -> IO CInt
  • 不,没有理由不这样。我在某处读到 FFI 函数不允许有副作用。我想我真正要问的是 1)这是真的吗 2)如果是这样,该怎么办。
  • 这不是真的。

标签: haskell haskell-ffi


【解决方案1】:

请注意,副作用的产生不是问题。重要的是“不纯”与“纯”功能。虽然getVal 不会引起副作用,但它依靠副作用来产生值,因为它咨询presentLocation。换句话说,它是一个不纯的函数。

Haskell 可以调用外部函数,无论它们是纯的还是不纯的,您只需要给它们适当的签名。必须为不纯函数提供 IO a 返回类型。可以给纯函数一个非IO 返回类型。 (当然,您也可以为纯函数提供 IO 返回类型,但您不必这样做,通常也不会这样做。)

例如,假设我们有一个简单的 C++“接口”:

int value = 0;   // Haskell code sets value directly
extern "C" int getValue() { return value; }  // and gets it with this

如果我们错误地尝试将getValue作为纯函数导入:

foreign import ccall "interface.cc &value" valuePtr :: Ptr CInt
foreign import ccall "interface.cc getValue" getValue :: CInt  -- **NO NO NO!!!**

并像这样测试它:

main :: IO ()
main = do
  print getValue
  poke valuePtr 5
  print getValue

我们得到不正确的输出:

0
0

相反,我们需要给getValue 一个类型IO CInt

foreign import ccall "interface.cc getValue" getValue :: IO CInt

对程序的其余部分进行适当的修改:

import Foreign
import Foreign.C

foreign import ccall "interface.cc &value" valuePtr :: Ptr CInt
foreign import ccall "interface.cc getValue" getValue :: IO CInt

main :: IO ()
main = do
  print =<< getValue
  poke valuePtr 5
  print =<< getValue

输出如预期:

0
5

请注意,只有 return 值应该被赋予 IO 类型。如果我们添加一个带参数的不纯函数,例如:

extern "C" int getMultValue(int scale) { return value*scale; }

然后你会使用:

foreign import ccall "interface.cc getMultValue" getMultValue :: CInt -> IO CInt

完整的节目:

// interface.cc
int value = 0;
extern "C" int getValue() { return value; }
extern "C" int getMultValue(int scale) { return value*scale; }

-- Main.hs
import Foreign
import Foreign.C

foreign import ccall "interface.cc &value" valuePtr :: Ptr CInt
foreign import ccall "interface.cc getValue" getValue :: IO CInt
foreign import ccall "interface.cc getMultValue" getMultValue :: CInt -> IO CInt

main :: IO ()
main = do
  print =<< getValue
  poke valuePtr 5
  print =<< getValue
  print =<< getMultValue 5

请注意,当所讨论的函数或变量实际上是方法/实例变量时,事情会变得更复杂一些。 Haskell 不直接支持使用 C++ 对象,因此您需要构建某种extern "C" 接口并将对象指针作为显式参数传递。如果您在设计过程中遇到麻烦,可以发布其他问题,我们会尽力提供帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-07
    • 1970-01-01
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 2011-09-29
    • 2015-02-03
    相关资源
    最近更新 更多