如何在Haskell FFI到C ++中编写纯字符串到字符串函数

我想通过Haskell FFI在C ++中实现一个函数,它应该有(最终)类型的String -> String 。 说,是否有可能在C ++中使用完全相同的签名重新实现以下函数?

import Data.Char
toUppers:: String -> String
toUppers s = map toUpper s

特别是,我想避免在返回类型中使用IO,因为为这个简单的任务引入杂质(我的意思是IO monad)在逻辑上是不必要的。 到目前为止,我所见过的一个C字符串的所有例子都涉及返回一个不能转换回纯String的IO或Ptr。

我想这样做的原因是,我觉得编组对FFI来说很混乱。 也许如果我可以修复上面最简单的情况(除了像int这样的基本类型),那么我可以在C ++端做任何我想要的数据解析,这应该很容易。

与我想在编组字符串之间进行的计算相比,解析的成本可以忽略不计。

提前致谢。


至少在某些时候需要涉及IO ,为C字符串分配缓冲区。 这里直接的解决方案可能是:

import Foreign
import Foreign.C
import System.IO.Unsafe as Unsafe

foreign import ccall "touppers" c_touppers :: CString -> IO ()
toUppers :: String -> String
toUppers s =
  Unsafe.unsafePerformIO $
    withCString s $ cs ->
      c_touppers cs >> peekCString cs

在我们使用withCString将Haskell字符串编组为缓冲区的情况下,将其更改为大写,最后将(更改!)缓冲区内容解组到新的Haskell字符串中。

另一种解决方案可能是将IO混乱委托给bytestring库。 无论如何,如果你对表演感兴趣,这可能是个好主意。 该解决方案大致如下所示:

import Data.ByteString.Internal

foreign import ccall "touppers2" 
  c_touppers2 :: Int -> Ptr Word8 -> Ptr Word8 -> IO ()
toUppers2 :: ByteString -> ByteString
toUppers2 s =
  unsafeCreate l $ p2 -> 
    withForeignPtr fp $ p1 ->
      c_touppers2 l (p1 `plusPtr` o) p2
 where (fp, o, l) = toForeignPtr s

这更优雅一些,因为我们现在不需要进行任何编组,只需转换指针即可。 另一方面,C ++端在两个方面发生了变化 - 我们必须处理非空终止的字符串(需要传递长度),现在必须写入不同的缓冲区,因为输入不再是副本。


作为参考,这里有两个适合上述导入的快捷C ++函数:

#include <ctype.h>
extern "C" void touppers(char *s) {
    for (; *s; s++) *s = toupper(*s);
}
extern "C" void touppers2(int l, char *s, char *t) {
    for (int i = 0; i < l; i++) t[i] = toupper(s[i]);
}
链接地址: http://www.djcxy.com/p/61181.html

上一篇: How to write a pure String to String function in Haskell FFI to C++

下一篇: Haskell FFI: Interfacing with simple C++?