1

If one has a constant value in a C/C++ header file that one wants to bring into a Haskell project:

static const char * Name = "Name_001";

How does one do so using Haskell's FFI functionality?

George
  • 6,927
  • 4
  • 34
  • 67
  • You don't, because 1) that's not a constant and 2) it's marked `static`, so every `.c` file that includes the header gets its own (private) copy. There's nothing for you to import there. – melpomene Sep 04 '17 at 03:43
  • Point granted on the `static` bit, but what do you mean it's not a constant? – George Sep 04 '17 at 03:44
  • `Name = "something else";` You can assign other values to it (or rather, to each of them (remember, multiple copies)). – melpomene Sep 04 '17 at 03:46
  • Then what effect does the `const` have? Is that the pointer itself is not constant, but the character that it points to are (so that you can change what `Name` points to, but whatever it points to cannot itself be mutated)? – George Sep 04 '17 at 03:47
  • http://c-faq.com/ansi/constptrconst.html – melpomene Sep 04 '17 at 03:54
  • @George `const char * ` is a pointer to a `const char`, not a constant pointer. In any case, I think Haskell's FFI only works with functions. Why not just make a simple `get_Name` function? That is pretty easy to do, even with a `static` variable. – Alec Sep 04 '17 at 03:58
  • @Alec You can import (the address of) variables: https://www.haskell.org/onlinereport/haskell2010/haskellch8.html#x15-1620008.5.1 – melpomene Sep 04 '17 at 04:11
  • @melpomene Cool - thanks for pointing that out! TIL. – Alec Sep 04 '17 at 04:13

1 Answers1

2

As @melpomene points out, static variables cannot be used for FFI. Every translation unit that includes this header file will create its own non-exported copy of the static variable. Unsurprisingly, that means that Name won't even be present in a compiled object file.

$ cat c_file.c
static const char * Name = "Name_001";
$ gcc -O -c c_file.c
$ objdump c_file.o

c_file.o: file format Mach-O 64-bit x86-64

SYMBOL TABLE:
$

Compare this to a non-static Name variable:

$ cat c_file.c
const char * Name = "Name_001";
$ gcc -O -c c_file.c
$ objdump c_file.o

c_file.o: file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000010 g       __DATA,__data  _Name
$

It goes without saying that you need Name to be in the symbol table to be imported in Haskell's FFI. Taking the second variant, importing Name becomes pretty easy:

{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.Storable
import Foreign.C.String
import Foreign.Ptr

foreign import ccall "&Name" c_Name :: Ptr CString

name :: IO String
name = peek c_Name >>= peekCString

main :: IO ()
main = do
  n <- name
  putStrLn ("Name: " ++ n)

If you want to convince me that Name is actually a constant string, you probably need char const * const = "Name_001;. Then, feel free to toss an unsafeDupablePerformIO in the definition of name:

{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.Storable
import Foreign.C.String
import Foreign.Ptr

import System.IO.Unsafe

foreign import ccall "&Name" c_Name :: Ptr CString

name :: String
name = unsafeDupablePerformIO (peek c_Name >>= peekCString)

main :: IO ()
main = putStrLn ("Name: " ++ name)

In either case, you get the expected output:

$ gcc -O -c c_file.c
$ ghc -O2 -Wall main.hs c_file.o
[1 of 1] Compiling Main             ( main.hs, main.o )
Linking main ...
$ ./main
Name: Name_001
arrowd
  • 33,231
  • 8
  • 79
  • 110
Alec
  • 31,829
  • 7
  • 67
  • 114