如何从Haskell调用C ++ Setter和Getter

时间:2019-05-30 05:36:20

标签: haskell haskell-ffi

我知道如何从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不在乎?

1 个答案:

答案 0 :(得分:2)

请注意,产生副作用不是问题。重要的是“纯”函数与“纯”函数。尽管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"接口并将对象指针作为显式参数传递。如果您在设计中遇到更多麻烦,也许提出其他问题,我们将尽力提供帮助。