c++ c ffi

Cómo mezclar C++ y C correctamente



ffi (2)

Tengo algunos problemas con esto: necesito escribir un contenedor C para una biblioteca C ++. Digamos que tengo 3 archivos:

  • wrapper.h

    typedef struct Foo Foo; Foo* create_foo();

  • wrapper.cpp

    extern "C" { #include "wrapper.h" } #include "foo.h" Foo* create_foo() { return new Foo; }

  • foo.h

    class Foo { public: Foo(); };

Esto compila bien:

clang++ -std=c++14 wrapper.cpp foo.h wrapper.h -shared -fPIC

clang++ -shared -o libbindings.so a.out

pero al compilar el programa que usa el contenedor C (es un compilador y está vinculado por el lenguaje de programación que usa el contenedor - Crystal), obtengo una referencia indefinida a create_foo () y un error de enlazador collect2: error: ld returned 1 exit status . ¿Cómo puedo depurar esto (y qué estoy haciendo mal)?


Aquí hay un ejemplo de trabajo:

wrapper.h ( consciente de C y C ++)

#ifndef WRAPPER_H_ #define WRAPPER_H_ #ifdef __cplusplus extern "C" { #endif typedef struct CPPClass CPPClass; CPPClass* CPPClass_new(); void CPPClass_do_something(CPPClass* cppclass); int CPPClass_get_state(CPPClass* cppclass); void CPPClass_delete(CPPClass* cppclass); #ifdef __cplusplus } #endif #endif /* WRAPPER_H_ */

wrapper.cpp (solo C ++)

#include "wrapper.h" class CPPClass { int state; public: CPPClass(): state(0) {} void do_something() { ++state; } int get_state() const { return state; } }; extern "C" CPPClass* CPPClass_new() { return new CPPClass; } extern "C" void CPPClass_do_something(CPPClass* cppclass) { cppclass->do_something(); } extern "C" int CPPClass_get_state(CPPClass* cppclass) { return cppclass->get_state(); } extern "C" void CPPClass_delete(CPPClass* cppclass) { delete cppclass; }

use-wrapper.c (solo C)

#include <stdio.h> #include "wrapper.h" int main(void) { CPPClass* cppclass = CPPClass_new(); if(!cppclass) { printf("ERROR: failed to create CPPClass:/n"); return 1; } printf("state: %d/n", CPPClass_get_state(cppclass)); CPPClass_do_something(cppclass); printf("state: %d/n", CPPClass_get_state(cppclass)); CPPClass_delete(cppclass); }

Compilar CPP

g++ -std=c++11 -shared -fPIC -o libwrapper.so wrapper.cpp

Compilar C

gcc -o use-wrapper use-wrapper.c -L. -lwrapper -lstdc++

Salida:

$ ./use-wrapper state: 0 state: 1

Espero que ayude.


Está creando un objeto compartido llamado a.out , luego otro objeto compartido llamado libbindings.so que aparentemente se vincula a a.out pero no hace referencia a nada de él. Ahora, si un conjunto de archivos de entrada no tiene símbolos indefinidos, no se buscan bibliotecas ni se agregan a la salida. Entonces libbindings.so es esencialmente una biblioteca vacía. Verificar:

% nm a.out | grep create_foo 00000000000006bc T create_foo % nm libbindings.so | grep create_foo %

Si tiene varios archivos fuente, debe compilar un archivo objeto a partir de cada fuente (use el indicador de compilación -c) (luego, opcionalmente, combine los objetos en una biblioteca estática --- omita este paso si no va a liberar bibliotecas estáticas) y luego compile un objeto compartido de objetos construidos previamente:

clang++ -c -fPIC foo.cpp clang++ -c -fPIC bar.cpp clang++ -shared -o libfoobar.so foo.o bar.o

Si solo tiene una fuente o muy pocos archivos fuente que pueda compilar fácilmente, puede compilar la biblioteca compartida en un solo paso:

clang++ -std=c++14 wrapper.cpp somethingelse.cpp -shared -fPIC -o libbindings.so

Esto no se recomienda para proyectos grandes.