Сгенерировать структуру C на основе сложного типа Haskell
Я пытаюсь использовать библиотеку Haskell в своем C-коде. Функция Haskell, которую я пытаюсь использовать, имеет тип String -> IO [Reference]
, где Reference
- довольно сложная структура (подробнее см. здесь).
На основе чтения различных частей документации кажется, что мне нужно будет сделать этот тип экземпляром Storable
, а также иметь аналогичную структуру, определенную в моем c-коде, чтобы иметь к ней доступ. Это похоже на очень много повторяющуюся работу для такого сложного типа. Есть ли способ автоматизировать это? Как можно было бы сделать такое?
Ответы
Ответ 1
Это зависит от вашего фактического использования, но...
Легче экспортировать Reference
как непрозрачный тип (через Foreign.StablePtr) и экспортировать функции getter для доступа к отдельным полям.
Пожалуйста, дайте мне знать, если вам нужна дополнительная информация, и я разберу ответ.
Ответ 2
Я написал небольшой инструмент (используя Template Haskell), который автоматически маршалирует любой тип данных, который состоит из примитивных типов (Int, Float, Double, Char, Bool), Список маршаллируемого типа и структуры, состоящие из marshallable types в соответствующий тип C.
- Примитивные типы становятся их C-образцами: Int → int, Float → float. Bool становится int.
- "Структуры" (данные S = S...) становятся указателями на структуру с маршаллированными членами структуры Haskell.
- Массивы ([S]) становятся указателями на структуру, состоящую из указателя на массив указателей на этот тип и int, указывая, сколько там элементов.
Итак, это:
data Test = Test [MyStruct] Int
data MyStruct = MyStruct Int
будет выглядеть так в C:
struct MyStruct {
int x;
}
struct ArrayStruct {
MyStruct** array;
int count;
}
struct Test {
ArrayStruct* arr_str;
int y;
}
Вот инструмент:
https://github.com/food2games/fieldmarshal
(Он также имеет часть С#, но вам понадобится HsFieldMarshal.) Он состоит из двух файлов, вам просто нужно скопировать их в свой код. Использование:
$(makeStorable ''YourType)
Обратите внимание, что он не сохраняет Storable-код автоматически для подтипов, поэтому, если у вас есть это:
data Type1 = Type1 Int Float
data Type2 = Type2 Int Type1
чем вы должны генерировать экземпляры Storable для каждого типа данных:
$(makeStorable ''Type1)
$(makeStorable ''Type2)
Также обратите внимание, что вы должны объявлять типы данных раньше, чем генерация экземпляра Storable (из-за TH). Так что это не сработает:
$(makeStorable ''Wrong)
data Wrong = Wrong Int
Это абсолютно не безупречно, достаточно для простых приложений, но если вы работаете с более сложным кодом, все может быть легко запутано.