6

私は Haskell FFI を介して C++ で関数を実装したいと考えてい String -> Stringます。たとえば、次の関数を C++ でまったく同じシグネチャで再実装することは可能ですか?

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

特に、この単純なタスクに不純物 (IO モナドを意味します) を導入することは論理的に不要であるため、戻り値の型に IO を持たないようにしたかったのです。これまでに見た C 文字列を含むすべての例では、純粋な に戻すことができない IO something または Ptr を返しStringます。

私がこれをやりたい理由は、マーシャリングがFFIで面倒だという印象を持っているからです。上記の最も単純なケース (int などのプリミティブ型以外) を修正できれば、C++ 側で必要なデータ解析を行うことができます。これは簡単なはずです。

解析のコストは、文字列との間のマーシャリングの間に実行したい計算と比較してごくわずかです。

前もって感謝します。

4

1 に答える 1

7

IOC文字列にバッファを割り当てるために、少なくともある時点で関与する必要があり ます。ここでの簡単な解決策は、おそらく次のようになります。

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

Haskell 文字列をバッファにマーシャリングするために使用withCStringした場所を大文字に変更し、最後に (変更された!) バッファの内容を新しい 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++ 側は 2 つの点で変更されています。null で終了しない可能性のある文字列を処理する必要があり (長さを渡す必要があります)、入力がコピーではないため、別のバッファーに書き込む必要があります。


参考までに、上記のインポートに適合する 2 つの簡単な 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]);
}
于 2013-06-03T11:01:04.400 に答える