I'm using bindings-DSL to help deal with some boilerplate in FFI declarations. But I find myself declaring groups of related functions that differ by only a couple of textual elements, and I'd really rather declare these with a macro. CPP or CPPHS seems the ideal choice for this, but I can't find any examples to its usage in the context of Haskell.
I've put this into what I approximately expect to work from my knowledge of C macros:
#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
Essentially, I'd like to define a foreign (opaque) type and 4 foreign functions upon expanding this macro. However, this does not work as it reads everything subsequent to the argument list as GHC pragmas, and fails.
I've already tried a couple different iterations of this, such as messing with spacing and putting everything on one line (wrapped in parentheses to distinguish separate macro calls).
How can I fix this to work? Answers that drop the bindings-DSL usage in favor of a direct translation are fine, but I definitely don't want to write all this out by hand.
I would also very much appreciate a few examples of this kind of CPP usage.
Here's the error message I get if I remove the space between the macro name and argument list:
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/
With the space, I get a much longer error message:
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
And this continues for every token. I'm fairly certain this just means the space shouldn't be included, but I'm not really sure what's going on.
EDIT:
New info.
I swapped methodology and I'm trying to generate the final foreign imports directly. The macro (I'll paste in a sec) passes the preprocessor with a couple of warnings, but actually trying to use the macro doesn't yet work:
#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
At the usage site, I'm trying to use it like:
#declare_vector_funcs int , i , CInt
To match the equivalent declaration on the C side. I expect it to generate a block that looks like:
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
But instead I get an error:
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
So I obviously need to add import Foreign.C
to the top, but even so, there's a deeper issue -- I can't treat these tokens as meaningless like I'd like to, something is trying to actually interpret them. Anyone have any ideas?
-XCPP
, right? What is the error you are getting? – crockeea