Предположим, у вас есть C API, который предоставляет структуру C
typedef struct A {
int i;
float f;
} A;
и функцию, которая его заполняет:
void getA(A* a);
Например, для некоторых это может быть метод полученияинформация из внутренних частей C API.
В Haskell структура C будет зеркально отражена как
data A = A {
i :: Int,
f :: Float
}
Экземпляр Storable
равен
instance Storable A where
sizeOf _ = {#sizeof A #}
alignment _ = {#alignof A #}
peek p = ...
poke p x = ...
Peek иpoke работает как обычно с прагмами {#get...#}
и {#set #}
, обработанными c2hs.
Функция Haskell getA :: IO A
должна выглядеть примерно так:
{#fun unsafe getA as getA {alloca- `A' peek*} -> `()'#}
, за исключением того, что это не работаетпотому что c2hs создает эту привязку:
foreign import ccall unsafe "include/A.h getA"
_getA'_ :: Ptr () -> IO ()
В качестве первого аргумента Ptr ()
.Это может быть отсортировано по
{#fun unsafe getA as getA {allocaA- `A' peekA*} -> `()'#}
peekA :: Ptr () -> IO A
peekA = peek . castPtr
allocaA :: (Ptr () -> IO a) -> IO a
allocaA f = alloca $ \(p :: Ptr A) -> f (castPtr p)
. Значение allocaA
важно, поскольку оно гарантирует, что память для A
выделяется вместо памяти только для ()
, если использовалось alloca
.
Хотя это работает, это несколько утомительно, и у вас есть гарантированные ошибки, если вы когда-нибудь забудете написать allocaXYZ
вместо alloca
.(Я только что видел много времени, потраченного на отслеживание одной такой ошибки.)
Я ожидал найти заклинание {#fun...#}
, которое производит
foreign import ccall unsafe "include/A.h getA"
_getA'_ :: Ptr A -> IO ()
, из которого все остальныеследовать естественным образом (обратите внимание на Ptr A
вместо Ptr ()
).Но, насколько я могу судить, есть только маршрут {allocXYZ- 'XYZ' peekXYZ*}
.
Так что вопрос: Можно ли сделать это лучше, используя c2hs
?