I want to foreign import a function from some c header, but how to deal with the stderr of type FILE* which defined as:
extern FILE* __stderrp;
#define stderr __stderrp
Maybe not precisely. I use c2hs for my ffi work, and already have:
{#pointer *FILE as File foreign finalizer fclose newtype#}
but I can not import stderr like this:
foreign import ccall "stdio.h stderr" stderr :: File
My c function has the signature:
void func(FILE*);
I can import func with c2hs:
{#fun func as ^ {`File'} -> `()'#}
I need to use stderr to run func:
func(stderr);
I am noob with the foreign import mechanism. It seems I can not import stderr in this way.
ps. Maybe I would wrap my func in a new function
void func2(void){func(stderr);}
This is a workaround, but seems not clean.
It's not unusual to require some kind of "shim" when writing FFI code for Haskell, and I'd encourage you to just write a helper function:
FILE* get_stderr() { return stderr; }
and use that (see example at the bottom of this answer).
However, I was able to get the following minimal example to work by using the vanilla FFI's support for static pointers -- it doesn't import stderr
directly, but imports a pointer to the stderr
pointer. This kind of import is not directly supported by c2hs, so the interface code is ugly, and I don't think there's any way to avoid having to fetch the stderr
pointer value in the IO monad, independent of whether or not you use c2hs.
// file.h
#include <stdio.h>
void func(FILE*);
// file.c
#include "file.h"
void func(FILE *f) {
fputs("Output to stderr!\n", f);
}
// File.chs
{-# LANGUAGE ForeignFunctionInterface #-}
module Main where
import Foreign
#include "file.h"
{#pointer *FILE as File newtype#}
{#fun func as ^ { `File' } -> `()'#}
foreign import ccall "&stderr" stderr_ptr :: Ptr (Ptr File)
main :: IO ()
main = do stderr <- File <$> peek stderr_ptr
func stderr
For comparison, this minimal example with the helper function looks much cleaner at the Haskell level:
// file.h
#include <stdio.h>
void func(FILE*);
FILE* get_stderr(void);
// file.c
#include "file.h"
void func(FILE *f) {
fputs("Output to stderr!\n", f);
}
FILE* get_stderr(void) {return stderr; }
// File.chs
{-# LANGUAGE ForeignFunctionInterface #-}
module Main where
#include "file.h"
{#pointer *FILE as File newtype#}
{#fun func as ^ { `File' } -> `()'#}
{#fun pure get_stderr as ^ {} -> `File'#}
main :: IO ()
main = func getStderr
Note that in both these examples, I removed your fclose
finalizer. You probably don't want Haskell arbitrarily deciding it's a good time to close stderr
on you.