How to write a String to String function in Haskell FFI for C ++

I want to implement a function in C ++ through Haskell FFI, which should have the (final) type String -> String . Say, is it possible to reimplement the following function in C ++ with the same signature?

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

In particular, I wanted to avoid having an I / O type in the return type, because impurity injection (by which I mean the IO monad) is logically not necessary for this simple task. All examples related to the C string that I have seen so far include returning an IO file or Ptr that cannot be converted back to a pure String .

The reason I want to do this is because I get the impression that marshaling is dirty with FFI. Maybe if I can fix the simplest case above (except for primitive types such as int), then I can do everything that is needed for parsing on the C ++ side, which should be easy.

The cost of parsing is negligible compared to the calculation I want to do between the marshalling lines to / from.

Thanks in advance.

+6
source share
1 answer

You need to enable IO , at least at some point, to allocate buffers for C-lines. A direct solution here would probably be the following:

 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 

Where we use withCString to sort a Haskell string into a buffer, change it to uppercase, and finally un-marshall (modified!) The contents of the buffer into a new Haskell string.

Another solution might be delegating messing with IO to the bytestring library. This may be a good idea if you are interested in performance. The solution will look something like this:

 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 

This is a bit more elegant since we don’t need to do any sorting now, just convert the pointers. On the other hand, the C ++ side changes in two ways: we need to process strings that do not contain zero (we need to pass the length), and now we have to write to another buffer, since the input is no longer a copy.


For reference, here are two quick and dirty C ++ functions that match the above imports:

 #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]); } 
+7
source

Source: https://habr.com/ru/post/946369/


All Articles