fpco / inline-c

284 stars 49 forks source link

Invalid identifier for non-typedef-ed structs #25

Closed nc6 closed 9 years ago

nc6 commented 9 years ago

The following block of code:

freeFoo :: Ptr Foo -- ^ Pointer to head of array
              -> IO ()
freeFoo arr = [C.block|
  struct foo *arr = $(struct foo *arr);
  foo_free(arr);
|]

fails with the following error:

    Exception when trying to run compile-time code:
      Identifier fromString: invalid string "struct foo"
    Code: template-haskell-2.10.0.0:Language.Haskell.TH.Quote.quoteExp
            C.block
            "\n\
            \  struct foo *arr = $(struct foo *arr);\n\
            \  foo_free(arr);\n"

Foo is storable and has been added to a custom context. I'm guessing this is an issue with not being able to parse non typedef-ed structs. Unfortunately the C project I'm trying to bind to has a policy of not using typedef for structs, so this is a rather big issue.

bitonic commented 9 years ago

That code you posted is invalid, since the C block is malformed (there is no return type).

In any case, what you want to do is perfectly fine, can you post a full example?

If I try with

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
import qualified Language.C.Inline as C
import Foreign.Ptr

C.context C.baseCtx

data Foo

freeFoo :: Ptr Foo -> IO ()
freeFoo arr = [C.block|
    struct foo *arr = $(struct foo *arr);
    foo_free(arr);
  |]

And then ghc -c foo.hs, I get

foo.hs:11:24:
    "foo.hs" (line 12, column 21):
unexpected "="
expecting "[", "(" or "{"

Which is due to the problem I mentioned (no return type).

If I fix that, for example with

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
import qualified Language.C.Inline as C
import Foreign.Ptr

C.context C.baseCtx

data Foo

freeFoo :: Ptr Foo -> IO ()
freeFoo arr = [C.block| int {
    struct foo *arr = $(struct foo *arr);
    foo_free(arr);
  }
  |]

I get

foo.hs:11:24:
    Could not resolve Haskell type for C type struct foo *

Which is telling you that inline-c does not know how to handle something of type struct foo *. To fix that error, you need to augment the context with an entry for struct foo *.

nc6 commented 9 years ago

Interesting. That's not the error I get, even if I fix the block declaration. Looks like I need to flesh out my example.

nc6 commented 9 years ago

Okay, here's a repository demonstrating the "invalid string" error:

https://github.com/nc6/inline-c-test

bitonic commented 9 years ago

Thanks, I can reproduce. Currently investigating.

bitonic commented 9 years ago

The problem is in your Context:

  C.ctxTypesTable = Map.fromList [
    (C.TypeName "struct a0_foo", [t| Foo |])
  ]

C.TypeName expects a C identifier, but struct a0_foo isn't one. To define a mapping from a struct to an Haskell type you would specify

  C.ctxTypesTable = Map.fromList [
    (C.Struct "a0_foo", [t| Foo |])
  ]

C.TypeName is to be used with typedefs. Maybe renaming it to C.Typedef would be nicer. @mboes , any thoughts?

I'll think about how we could make this error better...

nc6 commented 9 years ago

Aha! I see. Probably just adding some example contexts using C.Struct would help, I think.

Many thanks for your help!

Nick

On 1 Jul 2015, at 09:43, Francesco Mazzoli notifications@github.com wrote:

The problem is in your Context:

C.ctxTypesTable = Map.fromList [ (C.TypeName "struct a0_foo", [t| Foo |]) ] C.TypeName expects a C identifier, but struct a0_foo isn't one. To define a mapping from a struct to an Haskell type you would specify

C.ctxTypesTable = Map.fromList [ (C.Struct "a0_foo", [t| Foo |]) ] I'll think about how we could make this error better...

— Reply to this email directly or view it on GitHub https://github.com/fpco/inline-c/issues/25#issuecomment-117542103.