Haskell 如何使用来自 c 的 extern FILE*?

Haskell how to work with extern FILE* from c?

我想从一些 c 头文件中导入一个函数,但是如何处理定义为 FILE* 类型的标准错误:

extern FILE* __stderrp;
#define stderr __stderrp

也许不准确。我将 c2hs 用于我的 ffi 工作,并且已经拥有:

{#pointer *FILE as File foreign finalizer fclose newtype#}

但我不能像这样导入 stderr:

foreign import ccall "stdio.h stderr" stderr :: File

我的 c 函数具有签名:

void func(FILE*);

我可以用 c2hs 导入 func:

{#fun func as ^ {`File'} -> `()'#}

我需要使用 stderr 来 运行 func:

func(stderr);

我对外国进口机制一窍不通。看来我不能用这种方式导入stderr。

ps。也许我会把我的函数包装在一个新函数中

void func2(void){func(stderr);}

这是一种解决方法,但似乎不够干净。

在为 Haskell 编写 FFI 代码时需要某种 "shim" 的情况并不少见,我鼓励您只编写一个辅助函数:

FILE* get_stderr() { return stderr; }

并使用它(参见本答案底部的示例)。

但是,通过使用普通 FFI 对静态指针的支持,我能够让下面的最小示例工作——它不直接导入 stderr,而是导入一个指针 stderr 指针。 c2hs 不直接支持这种导入,所以接口代码很难看,而且我不认为有任何方法可以避免在 IO monad 中获取 stderr 指针值,无论是否独立你用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

相比之下,这个带有辅助函数的最小示例在 Haskell 级别看起来更清晰:

// 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

请注意,在这两个示例中,我删除了您的 fclose 终结器。您可能不希望 Haskell 武断地决定现在是关闭 stderr 的好时机。

使用 0.28.2 版本的 c2hs,以下代码有效:

-- lib.chs
{#pointer *FILE as File newtype#}
foreign import ccall "stdio.h &__stderrp" c_stderr :: Ptr (Ptr File) -- can not just use "stdio.h &stderr", this may cause a reference error

-- main.hs
stderr <- File <$> peek c_stderr
func stderr