您 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]);
}