Сгенерировать структуру 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

Это абсолютно не безупречно, достаточно для простых приложений, но если вы работаете с более сложным кодом, все может быть легко запутано.