CPP: макросы в Haskell

Я использую bindings-DSL, чтобы помочь разобраться с некоторыми шаблонами в декларациях FFI. Но я обнаружил, что объявляю группы связанных функций, которые отличаются только парой текстовых элементов, и я бы действительно объявил их с помощью макроса. CPP или CPPHS кажется идеальным выбором для этого, но я не могу найти никаких примеров его использования в контексте Haskell.

Я положил это в то, что я примерно ожидаю работать из моих знаний макросов C:

#define declare_vector_funcs (t, tn, ct) \ 
    #opaque_t vector_##t \
    #ccall create_std_vector##tn , IO (Ptr <vector_##t##>) \
    #ccall carray_to_std_vector##tn , Ptr ct -> CSize -> IO (Ptr <vector_##t##>) \
    #ccall std_vector##tn##_to_carray , Ptr <vector_##t##> -> IO (Ptr ct) \
    #ccall std_vector##tn##_length , Ptr <vector_##t##> -> IO CSize

По сути, я хотел бы определить внешний (непрозрачный) тип и 4 внешних функции при расширении этого макроса. Однако это не работает, так как считывает все, что следует за списком аргументов, как прагмы GHC, и завершается ошибкой.

Я уже пробовал пару разных итераций этого, например, возиться с пробелами и помещать все в одну строку (в скобках для различения отдельных вызовов макросов).

Как я могу исправить это, чтобы работать? Ответы, которые отбрасывают использование bindings-DSL в пользу прямого перевода, хороши, но я определенно не хочу писать все это вручную.

Я также был бы очень признателен за несколько примеров такого использования CPP.

Вот сообщение об ошибке, которое я получаю, если уберу пробел между именем макроса и списком аргументов:

CPP.hsc:13:39: error: '#' is not followed by a macro parameter
compiling dist/build/Foreign/CPP_hsc_make.c failed (exit code 1)
command was: /usr/bin/g++ -c dist/build/Foreign/CPP_hsc_make.c -o dist/build/Foreign/CPP_hsc_make.o -fno-stack-protector -D__GLASGOW_HASKELL__=708 -Dlinux_BUILD_OS=1 -Dx86_64_BUILD_ARCH=1 -Dlinux_HOST_OS=1 -Dx86_64_HOST_ARCH=1 -Iinclude/ -fpermissive -std=c++11 -fPIC -Idist/build/autogen -include dist/build/autogen/cabal_macros.h -I/usr/local/lib/x86_64-linux-ghc-7.8.2/bindings-DSL-1.0.21/include -I/usr/lib/ghc-7.8.2/base-4.7.0.0/include -I/usr/lib/ghc-7.8.2/integer-gmp-0.5.1.0/include -I/usr/lib/ghc-7.8.2/include -I/usr/lib/ghc-7.8.2/include/

С пробелом я получаю гораздо более длинное сообщение об ошибке:

dist/build/Foreign/CPP.hs:1:16:
    unknown flag in  {-# OPTIONS_GHC #-} pragma: tn,
dist/build/Foreign/CPP.hs:1:16:
    unknown flag in  {-# OPTIONS_GHC #-} pragma: ct)
dist/build/Foreign/CPP.hs:1:16:
    unknown flag in  {-# OPTIONS_GHC #-} pragma: #opaque_t
dist/build/Foreign/CPP.hs:1:16:
    unknown flag in  {-# OPTIONS_GHC #-} pragma: vector_##t

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


РЕДАКТИРОВАТЬ:

Новая информация

Я поменял методологию и пытаюсь напрямую генерировать конечный импорт. Макрос (я вставлю через секунду) передает препроцессору пару предупреждений, но на самом деле попытка использовать макрос пока не работает:

#define declare_vector_funcs(t, tn ,ct) \
    data C'vector_##t = C'vector_##t \
    foreign import ccall "create_std_vector##tn" c'create_std_vector##tn :: IO (Ptr C'vector_##t) \
    foreign import ccall "carray_to_std_vector##tn" c'carray_to_std_vector##tn :: Ptr ct -> CSize -> IO (Ptr vector_##t) \
    foreign import ccall "std_vector##tn##_to_carray" c'std_vector##tn##_to_carray :: Ptr vector_##t -> IO (Ptr ct) \
    foreign import ccall "std_vector##tn##_length" c'std_vector :: Ptr vector_##t -> IO CSize

На сайте использования я пытаюсь использовать его так:

#declare_vector_funcs int , i , CInt

Чтобы соответствовать эквивалентной декларации на стороне C. Я ожидаю, что это сгенерирует блок, который выглядит следующим образом:

data C'vector_int = C'vector_int 
foreign import ccall "create_std_vectori" c'create_std_vectori :: IO (Ptr C'vector_int) 
foreign import ccall "carray_to_std_vectori" c'carray_to_std_vectori :: Ptr CInt -> CSize -> IO (Ptr vector_int) 
foreign import ccall "std_vectori_to_carray" c'std_vectori_to_carray :: Ptr vector_int -> IO (Ptr CInt) 
foreign import ccall "std_vectori_length" c'std_vector :: Ptr vector_int -> IO CSize

Но вместо этого я получаю ошибку:

CPP.hsc: In function ‘int main(int, char**)’:
CPP.hsc:22:31: error: expected primary-expression before ‘int’
CPP.hsc:22:37: error: ‘i’ was not declared in this scope
CPP.hsc:22:41: error: ‘CInt’ was not declared in this scope
CPP.hsc:22:45: error: ‘hsc_declare_vector_funcs’ was not declared in this scope
CPP.hsc:23:31: error: expected primary-expression before ‘float’
CPP.hsc:23:39: error: ‘f’ was not declared in this scope
CPP.hsc:23:43: error: ‘CFloat’ was not declared in this scope
CPP.hsc:24:31: error: expected primary-expression before ‘double’
CPP.hsc:24:40: error: ‘d’ was not declared in this scope
CPP.hsc:24:44: error: ‘CDouble’ was not declared in this scope

Поэтому мне, очевидно, нужно добавить import Foreign.C к вершине, но даже в этом случае есть более глубокая проблема - я не могу относиться к этим токенам как к бессмысленным, как я бы хотел, что-то пытается на самом деле их интерпретировать. У кого-нибудь есть идеи?

3 ответа

Решение

То, что вы пытаетесь сделать, не совсем возможно, я не думаю, что когда вы определяете макрос в hsc файл будет встроен в ваш сгенерированный .hs файл, и если этот макрос вызывает .hsc макросы.. ну, те больше не будут доступны. Лучшее, что вы можете сделать:

module Test where

import Foreign.Ptr
import Foreign.C.Types

data T
data Tn
data Ct

#opaque_t vector_T
#ccall create_std_vectorTn , IO (Ptr <vector_T>)
#ccall carray_to_std_vectorTn , Ptr Ct -> CSize -> IO (Ptr <vector_T>) 
#ccall std_vectorTn_to_carray , Ptr <vector_T> -> IO (Ptr Ct) 
#ccall std_vectorTn_length , Ptr <vector_T> -> IO CSize

Если ваш компилятор C знает, где найти bindings.dsl.h тогда вы должны включить его в верхней части; мой не так я с компиляцией hsc2hs --include=<path-to-bindings.dsl.h> test.hsc, Это производит следующий файл:

{-# LINE 1 "test.hsc" #-}
module Test where
{-# LINE 2 "test.hsc" #-}

import Foreign.Ptr
import Foreign.C.Types

data T
data Tn
data Ct

data C'vector_T = C'vector_T

{-# LINE 11 "test.hsc" #-}
foreign import ccall "create_std_vectorTn" c'create_std_vectorTn
  :: IO (Ptr C'vector_T)
foreign import ccall "&create_std_vectorTn" p'create_std_vectorTn
  :: FunPtr (IO (Ptr C'vector_T))

{-# LINE 12 "test.hsc" #-}
foreign import ccall "carray_to_std_vectorTn" c'carray_to_std_vectorTn
  :: Ptr Ct -> CSize -> IO (Ptr C'vector_T)
foreign import ccall "&carray_to_std_vectorTn" p'carray_to_std_vectorTn
  :: FunPtr (Ptr Ct -> CSize -> IO (Ptr C'vector_T))

{-# LINE 13 "test.hsc" #-}
foreign import ccall "std_vectorTn_to_carray" c'std_vectorTn_to_carray
  :: Ptr C'vector_T -> IO (Ptr Ct)
foreign import ccall "&std_vectorTn_to_carray" p'std_vectorTn_to_carray
  :: FunPtr (Ptr C'vector_T -> IO (Ptr Ct))

{-# LINE 14 "test.hsc" #-}
foreign import ccall "std_vectorTn_length" c'std_vectorTn_length
  :: Ptr C'vector_T -> IO CSize
foreign import ccall "&std_vectorTn_length" p'std_vectorTn_length
  :: FunPtr (Ptr C'vector_T -> IO CSize)

{-# LINE 15 "test.hsc" #-}

Похоже, что определение собственного макроса - это путь:

mymacro.h

#define hsc_declare_vector_funcs(t,tn,ct)\
  hsc_opaque_t(vector_##t)\
  hsc_ccall(create_std_vector##tn ,IO (Ptr <vector_##t>))\
  hsc_ccall(carray_to_std_vector##tn , Ptr ct -> CSize -> IO (Ptr <vector_##t>)) \
  hsc_ccall(std_vector##tn##_to_carray , Ptr <vector_##t> -> IO (Ptr ct)) \
  hsc_ccall(std_vector##tn##_length , Ptr <vector_##t> -> IO CSize)

test.hsc

module Test where

#include "mymacro.h"

import Foreign.Ptr
import Foreign.C.Types

#declare_vector_funcs int, i, CInt

test.hs

module Test where

import Foreign.Ptr
import Foreign.C.Types

data C'vector_int = C'vector_int
foreign import ccall "create_std_vectori" c'create_std_vectori
  :: IO (Ptr C'vector_int)
foreign import ccall "&create_std_vectori" p'create_std_vectori
  :: FunPtr (IO (Ptr C'vector_int))
foreign import ccall "carray_to_std_vectori" c'carray_to_std_vectori
  :: Ptr CInt -> CSize -> IO (Ptr C'vector_int)
foreign import ccall "&carray_to_std_vectori" p'carray_to_std_vectori
  :: FunPtr (Ptr CInt -> CSize -> IO (Ptr C'vector_int))
foreign import ccall "std_vectori_to_carray" c'std_vectori_to_carray
  :: Ptr C'vector_int -> IO (Ptr CInt)
foreign import ccall "&std_vectori_to_carray" p'std_vectori_to_carray
  :: FunPtr (Ptr C'vector_int -> IO (Ptr CInt))
foreign import ccall "std_vectori_length" c'std_vectori_length
  :: Ptr C'vector_int -> IO CSize
foreign import ccall "&std_vectori_length" p'std_vectori_length
  :: FunPtr (Ptr C'vector_int -> IO CSize)

Следующий пример компилируется:

{-# LANGUAGE CPP #-}

#define MYMACRO 3
#define MAX(a,b) (if ((a) < (b)) \
              then (b) \
              else (a))

main = print $ show (MAX(MYMACRO,4) :: Int)

Мне нужны скобки вокруг макроса кода, чтобы (также) сработало следующее:

main = print $ show MAX(MYMACRO,4)

Если вы хотите сделать это с помощью bindings-DSL, посмотрите, как это было сделано в bindings-gobject. Вы должны использовать макросы hsc_*.

Другие вопросы по тегам