GNU/Linux >> Linux Esercitazione >  >> Linux

Libreria condivisa dinamica C++ su Linux

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

miaclasse.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 );
}

Su Mac OS X, compila con:

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

Su Linux, compila con:

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

Se questo fosse per un sistema di plug-in, useresti MyClass come classe base e definiresti tutte le funzioni richieste virtual. L'autore del plugin deriverebbe quindi da MyClass, sovrascriverebbe i virtuals e implementerebbe create_object e destroy_object . La tua applicazione principale non dovrebbe essere modificata in alcun modo.


Quanto segue mostra un esempio di una libreria di classi condivisa shared.[h,cpp] e un modulo main.cpp che utilizza la libreria. È un esempio molto semplice e il makefile potrebbe essere migliorato molto. Ma funziona e può aiutarti:

shared.h definisce la classe:

class myclass {
   int myx;

  public:

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

shared.cpp definisce le funzioni getx/setx:

#include "shared.h"

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

main.cpp usa la classe,

#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;
}

e il makefile che genera libshared.so e collega main con la libreria condivisa:

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

Per eseguire effettivamente 'main' e collegarti a libshared.so probabilmente dovrai specificare il percorso di caricamento (o inserirlo in /usr/local/lib o simile).

Quanto segue specifica la directory corrente come percorso di ricerca per le librerie ed esegue main (sintassi bash):

export LD_LIBRARY_PATH=.
./main

Per vedere che il programma è linkato con libshared.so puoi provare ldd:

LD_LIBRARY_PATH=. ldd main

Stampe sulla mia macchina:

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

Oltre alle risposte precedenti, vorrei sensibilizzare sul fatto che dovresti usare l'idioma RAII (Resource Acquisition Is Initialisation) per essere sicuro sulla distruzione del gestore.

Ecco un esempio funzionante completo:

Dichiarazione di interfaccia:Interface.hpp :

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

using Base_creator_t = Base *(*)();

Contenuto della libreria condivisa:

#include "Interface.hpp"

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

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

Gestore dinamico della libreria condivisa: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);
        }
    }
};

Codice cliente:

#include "Derived_factory.hpp"

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

Nota:

  • Ho inserito tutto nei file di intestazione per concisione. Nella vita reale dovresti ovviamente dividere il tuo codice tra .hpp e .cpp file.
  • Per semplificare, ho ignorato il caso in cui vuoi gestire un new /delete sovraccarico.

Due articoli chiari per avere maggiori dettagli:

  • C++ dlopen mini tutorial
  • Caricamento dinamico C++ di oggetti condivisi in fase di runtime

Linux
  1. Compilazione dinamica della libreria condivisa con g++

  2. Convertire una libreria statica in una libreria condivisa?

  3. Scoperta della dipendenza della libreria dinamica su Mac OS e Linux

  4. Rimozione delle librerie condivise di Linux

  5. Dove inserisco le librerie di terze parti per configurare un ambiente di sviluppo Linux C++?

Come installare la libreria Ncurses in Linux

Introduzione alle librerie condivise Linux (come creare librerie condivise)

Come inizializzare una libreria condivisa su Linux

Come compilare una libreria dinamica per un'applicazione JNI su Linux?

Come posso visualizzare l'elenco delle funzioni che una libreria condivisa di Linux sta esportando?

Rileva Windows o Linux in C, C++