2011-12-31 3 views
17

Quiero construir una biblioteca dinámica que contenga funciones haskell. Trabajo en Linux y quiero llamar a esta biblioteca dinámica desde código C++.Creando una biblioteca dinámica con haskell y usándola desde C++

que se utiliza en el ejemplo http://wiki.python.org/moin/PythonVsHaskell y tienen los siguientes archivos:

Test.hs:

{-# LANGUAGE ForeignFunctionInterface #-} 
module Test where 

import Foreign.C.Types 

hsfun :: CInt -> IO CInt 
hsfun x = do 
    putStrLn "Hello World" 
    return (42 + x) 

foreign export ccall 
    hsfun :: CInt -> IO CInt 

module_init.c:

#define CAT(a,b) XCAT(a,b) 
#define XCAT(a,b) a ## b 
#define STR(a) XSTR(a) 
#define XSTR(a) #a 

#include <HsFFI.h> 

extern void CAT (__stginit_, MODULE) (void); 

static void library_init (void) __attribute__ ((constructor)); 
static void 
library_init (void) 
{ 
    /* This seems to be a no-op, but it makes the GHCRTS envvar work. */ 
    static char *argv[] = { STR (MODULE) ".so", 0 }, **argv_ = argv; 
    static int argc = 1; 

    hs_init (&argc, &argv_); 
    hs_add_root (CAT (__stginit_, MODULE)); 
} 

static void library_exit (void) __attribute__ ((destructor)); 
static void 
library_exit (void) 
{ 
    hs_exit(); 
} 

Ahora puedo compilar estos archivos a una dinámica biblioteca:

$ ghc -dynamic -shared -fPIC -optc '-DMODULE=Test' Test.hs module_init.c -o libTest.so 
[1 of 1] Compiling Test    (Test.hs, Test.o) 
Linking libTest.so ... 

Esto crea entre otras cosas el archivo Test_stub.h:

#include "HsFFI.h" 
#ifdef __cplusplus 
extern "C" { 
#endif 
extern HsInt32 hsfun(HsInt32 a1); 
#ifdef __cplusplus 
} 
#endif 

y Test_stub.c:

#define IN_STG_CODE 0 
#include "Rts.h" 
#include "Stg.h" 
#ifdef __cplusplus 
extern "C" { 
#endif 

extern StgClosure Test_zdfhsfunzua165_closure; 
HsInt32 hsfun(HsInt32 a1) 
{ 
Capability *cap; 
HaskellObj ret; 
HsInt32 cret; 
cap = rts_lock(); 
cap=rts_evalIO(cap,rts_apply(cap,(HaskellObj)runIO_closure,rts_apply(cap,&Test_zdfhsfunzua165_closure,rts_mkInt32(cap,a1))) ,&ret); 
rts_checkSchedStatus("hsfun",cap); 
cret=rts_getInt32(ret); 
rts_unlock(cap); 
return cret; 
} 
static void stginit_export_Test_zdfhsfunzua165() __attribute__((constructor)); 
static void stginit_export_Test_zdfhsfunzua165() 
{getStablePtr((StgPtr) &Test_zdfhsfunzua165_closure);} 
#ifdef __cplusplus 
} 
#endif 

Entonces creo un main.cpp cpp:

#include "Test_stub.h" 

#include <iostream> 

using namespace std; 

int main() 
{ 
    cout << hsfun(5); 
} 

y desea compilar y vincularlo. Pero cuando llamo g ++, que dice:

$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp 
/tmp/ccFP2AuB.o: In function `main': 
main.cpp:(.text+0xa): undefined reference to `hsfun' 
collect2: ld gab 1 als Ende-Status zurück 

por lo que añade el archivo Test_stub.o a la línea de comandos (aunque creo que la función hsfun ya debe estar definido en libTest.so que se añade a través de la -lTest . parámetro no creo, yo debería vincular el archivo Test_stub.o en el ejecutable porque quiero utilizar la vinculación dinámica), pero esto tampoco funciona:

$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp Test_stub.o 
Test_stub.o: In function `hsfun': 
Test_stub.c:(.text+0x9): undefined reference to `rts_lock' 
Test_stub.c:(.text+0x16): undefined reference to `rts_mkInt32' 
Test_stub.c:(.text+0x1d): undefined reference to `Test_zdfhsfunzua165_closure' 
Test_stub.c:(.text+0x28): undefined reference to `rts_apply' 
Test_stub.c:(.text+0x2f): undefined reference to `base_GHCziTopHandler_runIO_closure' 
Test_stub.c:(.text+0x3a): undefined reference to `rts_apply' 
Test_stub.c:(.text+0x4a): undefined reference to `rts_evalIO' 
Test_stub.c:(.text+0x5c): undefined reference to `rts_checkSchedStatus' 
Test_stub.c:(.text+0x66): undefined reference to `rts_getInt32' 
Test_stub.c:(.text+0x70): undefined reference to `rts_unlock' 
Test_stub.o: In function `stginit_export_Test_zdfhsfunzua165': 
Test_stub.c:(.text.startup+0x3): undefined reference to `Test_zdfhsfunzua165_closure' 
Test_stub.c:(.text.startup+0x8): undefined reference to `getStablePtr' 
collect2: ld gab 1 als Ende-Status zurück 

¿tengo que vincular la Test_stub .o? ¿Si es así por qué? ¿Y qué argumentos debo pasar al enlazador?

+0

No sé los detalles; pero ciertamente necesita vincular el tiempo de ejecución de Haskell también (en particular su recolector de basura). –

+1

"$ g ++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp" ¿Tal vez funcione si coloca los indicadores del enlazador al final de la línea de comando? –

+0

@Daniel: Es la segunda vez que veo a alguien sugiriendo que coloque banderas enlazadoras al final, y la primera vez parece que también solucionó el problema. ¿Porque eso? Pensé que el posicionamiento de las banderas no importa? – Xeo

Respuesta

9

Probablemente sea más fácil que luchar con g ++ es dejar GHC hacer el trabajo,

GHC main.cpp -o hithere -L. -lTest -lstdC++

hizo el trabajo por mí después de crear la biblioteca compartida de la manera en que lo hizo. Lo he probado con 7.2.2 y 7.0.2, ambos funcionan aquí.

+2

La interfaz de Haskell es un módulo de un proyecto más grande y no quiero compilar todo el proyecto de C++ con ghc. – Heinzi

+0

Eso tiene sentido. Podría tratar de capturar los indicadores del enlazador necesarios haciendo que ghc compile el ejemplo con una gran cantidad de verbosidad y redireccione el stderr a un archivo.Sin embargo, le dará una lista bastante larga de elementos a ser vinculados. –

+1

Lo tengo trabajando usando su solución. Cuando paso el parámetro -v a ghc, imprime los parámetros del enlazador usado en la línea de comando. – Heinzi

Cuestiones relacionadas