GNU/Linux >> Tutoriales Linux >  >> Linux

Biblioteca compartida dinámica de C++ en Linux

miclase.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

miclase.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;
}

usuario_clase.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, compila con:

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

En Linux, compila con:

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

Si esto fuera para un sistema de complementos, usaría MyClass como clase base y definiría todas las funciones requeridas virtuales. El autor del complemento luego se derivaría de MyClass, anularía los virtuales e implementaría create_object y destroy_object . No será necesario cambiar su aplicación principal de ninguna manera.


A continuación, se muestra un ejemplo de una biblioteca de clases compartida shared.[h,cpp] y un módulo main.cpp que usa la biblioteca. Es un ejemplo muy simple y el archivo MAKE se podría hacer mucho mejor. Pero funciona y puede ayudarte:

shared.h define la clase:

class myclass {
   int myx;

  public:

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

shared.cpp define las funciones getx/setx:

#include "shared.h"

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

main.cpp usa 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 main 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 enlazar con libshared. probablemente necesitará especificar la ruta de carga (o ponerla en /usr/local/lib o similar).

Lo siguiente especifica el directorio actual como la ruta de búsqueda de bibliotecas y ejecuta main (sintaxis bash):

export LD_LIBRARY_PATH=.
./main

Para ver que el programa está vinculado con libshared.así que puedes 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)

Además de las respuestas anteriores, me gustaría crear conciencia sobre el hecho de que debe usar el idioma RAII (Adquisición de recursos es inicialización) para estar seguro sobre la destrucción del controlador.

Aquí hay un ejemplo de trabajo completo:

Declaración de interfaz:Interface.hpp :

class Base {
public:
    virtual ~Base() {}
    virtual void foo() const = 0;
};

using Base_creator_t = Base *(*)();

Contenido de la biblioteca compartida:

#include "Interface.hpp"

class Derived: public Base {
public:
    void foo() const override {}
};

extern "C" {
Base * create() {
    return new Derived;
}
}

Controlador de biblioteca compartida dinámica:Derived_factory.hpp :

#include "Interface.hpp"
#include <dlfcn.h>

class Derived_factory {
public:
    Derived_factory() {
        handler = dlopen("libderived.so", RTLD_NOW);
        if (! handler) {
            throw std::runtime_error(dlerror());
        }
        Reset_dlerror();
        creator = reinterpret_cast<Base_creator_t>(dlsym(handler, "create"));
        Check_dlerror();
    }

    std::unique_ptr<Base> create() const {
        return std::unique_ptr<Base>(creator());
    }

    ~Derived_factory() {
        if (handler) {
            dlclose(handler);
        }
    }

private:
    void * handler = nullptr;
    Base_creator_t creator = nullptr;

    static void Reset_dlerror() {
        dlerror();
    }

    static void Check_dlerror() {
        const char * dlsym_error = dlerror();
        if (dlsym_error) {
            throw std::runtime_error(dlsym_error);
        }
    }
};

Código de cliente:

#include "Derived_factory.hpp"

{
    Derived_factory factory;
    std::unique_ptr<Base> base = factory.create();
    base->foo();
}

Nota:

  • Pongo todo en archivos de encabezado para que sea conciso. En la vida real, por supuesto, deberías dividir tu código entre .hpp y .cpp archivos.
  • Para simplificar, ignoré el caso en el que desea manejar un new /delete sobrecarga.

Dos artículos claros para obtener más detalles:

  • C++ dlopen mini tutorial
  • Carga dinámica de C++ de objetos compartidos en tiempo de ejecución

Linux
  1. Cómo instalar la biblioteca Ncurses en Linux

  2. ¿Convertir una biblioteca estática en una biblioteca compartida?

  3. Cómo inicializar una biblioteca compartida en Linux

  4. ¿Dónde coloco bibliotecas de terceros para configurar un entorno de desarrollo de C++ Linux?

  5. Detectar Windows o Linux en C, C++

¿Cómo compilar una biblioteca dinámica para una aplicación JNI en Linux?

¿Cómo veo la lista de funciones que exporta una biblioteca compartida de Linux?

¿Cómo hacer el control de versiones de una biblioteca compartida en Linux?

Cómo configurar googleTest como una biblioteca compartida en Linux

Vincular una biblioteca compartida con otra biblioteca compartida en Linux

Cmake en Windows no agrega rutas de biblioteca compartidas (funciona en Linux)