2009-01-30 28 views
135

Esta es una continuación de Dynamic Shared Library compilation with g++.Biblioteca compartida dinámica de C++ en Linux

Estoy tratando de crear una biblioteca de clases compartida en C++ en Linux. Puedo hacer que la biblioteca compile y puedo llamar a algunas de las funciones (que no son de clase) usando los tutoriales que encontré here y here. Mis problemas comienzan cuando trato de usar las clases que están definidas en la biblioteca. El segundo tutorial al que he vinculado muestra cómo cargar los símbolos para crear objetos de las clases definidas en la biblioteca, pero se detiene antes de usando esos objetos para realizar cualquier trabajo.

¿Alguien sabe de un tutorial más completo para crear bibliotecas de clases C++ compartidas que también muestra cómo usar esas clases en un ejecutable independiente? Un tutorial muy simple que muestra creación de objetos, uso (getters y setters simples estarían bien), y la eliminación sería fantástica. Un enlace o una referencia a algún código fuente abierto que ilustre el uso de una biblioteca de clases compartida sería igualmente bueno.


Aunque las respuestas de codelogic y nimrodm hacer el trabajo, sólo quería añadir que he recogido una copia de Beginning Linux Programming ya hacer esta pregunta, y su primer capítulo tiene código de ejemplo C y buenas explicaciones para la creación y uso ambas bibliotecas estáticas y compartidas. Estos ejemplos están disponibles a través de la Búsqueda de libros de Google en an older edition of that book.

+0

No estoy seguro de entender lo que quiere decir con "usarlo", una vez que se devuelve un puntero al objeto, puede usarlo como si usara cualquier otro puntero a un objeto. – codelogic

+0

El artículo al que he vinculado muestra cómo crear un puntero de función a una función de fábrica de objetos utilizando dlsym. No muestra la sintaxis para crear y usar objetos de la biblioteca. –

+1

Necesitará el archivo de encabezado que describe la clase. ¿Por qué crees que tienes que usar "dlsym" en lugar de simplemente dejar que el sistema operativo encuentre y vincule la biblioteca en el momento de la carga? Avíseme si necesita un ejemplo simple. – nimrodm

Respuesta

119

MyClass.h

#ifndef __MYCLASS_H__ 
#define __MYCLASS_H__ 

class MyClass 
{ 
public: 
    MyClass(); 

    /* use virtual otherwise linker will try to perform static linkage */ 
    virtual void DoSomething(); 

private: 
    int x; 
}; 

#endif 

myclass.cc

#include "myclass.h" 
#include <iostream> 

using namespace std; 

extern "C" MyClass* create_object() 
{ 
    return new MyClass; 
} 

extern "C" void destroy_object(MyClass* object) 
{ 
    delete object; 
} 

MyClass::MyClass() 
{ 
    x = 20; 
} 

void MyClass::DoSomething() 
{ 
    cout<<x<<endl; 
} 

class_user.cc

#include <dlfcn.h> 
#include <iostream> 
#include "myclass.h" 

using namespace std; 

int main(int argc, char **argv) 
{ 
    /* on Linux, use "./myclass.so" */ 
    void* handle = dlopen("myclass.so", RTLD_LAZY); 

    MyClass* (*create)(); 
    void (*destroy)(MyClass*); 

    create = (MyClass* (*)())dlsym(handle, "create_object"); 
    destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object"); 

    MyClass* myClass = (MyClass*)create(); 
    myClass->DoSomething(); 
    destroy(myClass); 
} 

En Mac OS X, compilar con:

g++ -dynamiclib -flat_namespace myclass.cc -o myclass.so 
g++ class_user.cc -o class_user 

En Linux, compilar con:

g++ -fPIC -shared myclass.cc -o myclass.so 
g++ class_user.cc -ldl -o class_user 

Si esto fuera por un sistema de plugins, utilizaría MiClase como clase base y definir todas las funciones necesarias virtual. El autor del complemento derivaría entonces de MyClass, anularía los virtuales e implementaría create_object y destroy_object. No es necesario cambiar su aplicación principal de ninguna manera.

+6

Estoy en el proceso de intentar esto, pero solo tiene una pregunta ¿Es estrictamente necesario usar void *, o podría la función create_object devolver MyClass * en su lugar? No te estoy pidiendo que cambies esto por mí, solo me gustaría saber si hay una razón para usar uno sobre el otro. –

+0

Puede ser MyClass *, no hay razón para que sea nulo *, lo he actualizado. – codelogic

+1

Gracias, probé esto y funcionó como está en Linux desde la línea de comandos (una vez que hice el cambio sugerido en los comentarios del código). Aprecio tu tiempo. –

48

A continuación, se muestra un ejemplo de una biblioteca de clases compartida compartida. [H, cpp] y un módulo main.cpp que utiliza la biblioteca. Es un ejemplo muy simple y el archivo MAKE podría mejorar mucho. Pero funciona y puede ayudarle a:

SHARED.H define la clase:

class myclass { 
    int myx; 

    public: 

    myclass() { myx=0; } 
    void setx(int newx); 
    int getx(); 
}; 

shared.cpp define los getX/setx funciones:

#include "shared.h" 

void myclass::setx(int newx) { myx = newx; } 
int myclass::getx() { return myx; } 

main.cpp utiliza la clase,

#include <iostream> 
#include "shared.h" 

using namespace std; 

int main(int argc, char *argv[]) 
{ 
    myclass m; 

    cout << m.getx() << endl; 
    m.setx(10); 
    cout << m.getx() << endl; 
} 

y el makefile que genera libshared.so y vincula principal con la biblioteca compartida:

main: libshared.so main.o 
    $(CXX) -o main main.o -L. -lshared 

libshared.so: shared.cpp 
    $(CXX) -fPIC -c shared.cpp -o shared.o 
    $(CXX) -shared -Wl,-soname,libshared.so -o libshared.so shared.o 

clean: 
    $rm *.o *.so 

Para ejecutar 'main' y vincular con libshared.so probablemente necesite especificar la ruta de la carga (o ponerla en/usr/local/lib o similar).

A continuación se especifica el directorio actual como la ruta de búsqueda de bibliotecas y corre principal (sintaxis bash):

export LD_LIBRARY_PATH=. 
./main 

ver que el programa está vinculado con libshared.so puede probar LDD:

LD_LIBRARY_PATH=. ldd main 

Imprime en mi máquina:

~/prj/test/shared$ LD_LIBRARY_PATH=. ldd main 
    linux-gate.so.1 => (0xb7f88000) 
    libshared.so => ./libshared.so (0xb7f85000) 
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e74000) 
    libm.so.6 => /lib/libm.so.6 (0xb7e4e000) 
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xb7e41000) 
    libc.so.6 => /lib/libc.so.6 (0xb7cfa000) 
    /lib/ld-linux.so.2 (0xb7f89000) 
+1

Esto parece (para mi ojo muy desentrenado) vincular estáticamente libshared.so al ejecutable, en lugar de usar enlaces dinámicos en el tiempo de ejecución. ¿Estoy en lo correcto? –

+9

No. Esto es un enlace dinámico estándar de Unix (Linux). Una biblioteca dinámica tiene la extensión ".so" (Objeto compartido) y está vinculada con el ejecutable (principal en este caso) en el tiempo de carga, cada vez que se carga el principal. La vinculación estática se produce en el momento del enlace y utiliza bibliotecas con la extensión ".a" (archivo). – nimrodm

+8

Esto está enlazado dinámicamente en _build_ time. En otras palabras, necesitas conocimiento previo de la biblioteca con la que estás enlazando (por ejemplo, vinculando a 'dl' para dlopen). Esto es diferente de una carga dinámica de una biblioteca, basada, por ejemplo, en un nombre de archivo especificado por el usuario, donde no se necesita conocimiento previo. – codelogic

8

Básicamente, debe incluir las clas s 'archivo de encabezado en el código donde desea utilizar la clase en la biblioteca compartida. Luego, cuando se vincula, use the '-l' flag para vincular su código con la biblioteca compartida. Por supuesto, esto requiere que el .so esté donde el SO puede encontrarlo.Ver 3.5. Installing and Using a Shared Library

Usar dlsym es para cuando no sabe en tiempo de compilación qué biblioteca desea usar. Eso no suena como si fuera el caso aquí. Tal vez la confusión es que Windows llama a las bibliotecas cargadas dinámicamente ya sea que realice el enlace en compilación o en tiempo de ejecución (con métodos análogos). Si es así, entonces puedes pensar en dlsym como el equivalente de LoadLibrary.

Si realmente necesita cargar dinámicamente las bibliotecas (es decir, son plug-ins), entonces this FAQ debería ayudar.

+1

La razón por la que necesito una biblioteca compartida dinámica es que también la llamaré desde el código Perl. Puede ser una idea errónea por mi parte que también debo llamarlo dinámicamente desde otros programas C++ que estoy desarrollando. –

+0

Nunca intenté Perl integrado y C++, pero creo que debe usar XS: http://www.johnkeiser.com/perl-xs-c++html –

Cuestiones relacionadas