Haskell FFI: обертывание структуры C, содержащей отдельно выделенную строку (char *) - PullRequest
0 голосов
/ 29 ноября 2018

Предположим, у вас есть C-struct

typedef struct {
  uint32_t num;
  char*    str;
} MyStruct;

и функция f, которая над ним работает,

void f(MyStruct* p);

API C требует, чтобы char*выделите достаточный буфер перед вызовом f:

char buf[64];   //the C API docs say 64
MyStruct s = {1, buf};
f(s);  // would go badly if MyStruct.str isn't alloc'ed

(Обратите внимание, что поле num не имеет цели в этом построенном примере. Оно просто предотвращает тривиальное решение с использованием CString и CStringLen.)

Вопрос в том, как написать FFI на Haskell для такого рода API C.

Я пришел к следующему: начните с

data MyStruct = MyStruct {
    num :: Word32,
    str :: String
} deriving Show

и напишите сохраняемый экземпляр.Моя идея состоит в том, чтобы выделить 64 байта в конце, который будет служить буфером для строки:

instance Storable MyStruct where
    sizeOf _ = 8{-alignment!-} + sizeOf (nullPtr :: CString) + 64{-buffer-}
    alignment _ = 8

poke должен изменить указатель в str, чтобы он указывал на выделенный буфер, а затем строку Haskellдолжен быть скопирован в него.Я делаю это с withCStringLen:

poke p x = do
    pokeByteOff p 0 (num x)
    poke strPtr bufPtr
    withCStringLen (str x) $ \(p',l) -> copyBytes bufPtr p' (l+1) -- not sure about the +1
    where strPtr = castPtr $ plusPtr p 8 :: Ptr CString
          bufPtr = castPtr $ plusPtr p 16 :: CString

Наконец вот peek, что довольно просто:

peek p = MyStruct 
     <$> peek (castPtr p)
     <*> peekCAString (castPtr $ plusPtr p 16)

Все это работает, но я нахожу это довольно уродливым. Это способ сделать это, или есть лучший способ?

Если кто-то захочет поиграть с ним, проблема с маленькой игрушкой на github .

Обновление

Как указано chi, необходимо соблюдать следующее предостережение: использование жестко закодированных выравниваний и смещений является плохой практикой.Они хрупки и зависят от платформы / компилятора.Вместо этого следует использовать такие инструменты, как c2hsc , c2hs или bindings-dsl или greencard и т. Д.

1 Ответ

0 голосов
/ 29 ноября 2018

Хотя ваше решение кажется мне довольно приятным (вы прячете эту память в экземпляре Storable, чтобы пользователь сам не потрудился найти буфер памяти), вы также можете имитировать решение C с помощью allocaBytes.

...