Haskell FFi с c2hs: лучшая компоновка структур - PullRequest
0 голосов
/ 14 декабря 2018

Предположим, у вас есть 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?

1 Ответ

0 голосов
/ 10 июля 2019

Используете ли вы {#pointer ...#} крючки?Поскольку вы не рассматриваете A как непрозрачный указатель (т. Е. i и f доступны нормально, а экземпляр Storable написан явно), вам нужно использовать форму стрелки:

{#pointer *A as APtr -> A#}

В этот момент вам все равно придется использовать функции alloca / peek для маршала A, но ваш первый идеальный хук {#fun ...#} должен работать как написано.(Вы игнорируете тип APtr; он появляется в сгенерированном коде, но это не обязательно в файле *.chs.)

Также обратите внимание, что вам нужно добавить это определение указателя в каждый файлA используется в;даже если вы экспортируете APtr из одного основного файла, вам все равно нужно добавлять {#pointer *A as APtr -> A nocode#} везде, где вы его используете.

...