Poiché ho osservato un comportamento strano delle variabili globali nelle mie librerie caricate dinamicamente, ho scritto il seguente test.Librerie caricate dinamiche e simboli globali condivisi
In un primo momento abbiamo bisogno di una libreria collegata in modo statico: L'intestazione test.hpp
#ifndef __BASE_HPP
#define __BASE_HPP
#include <iostream>
class test {
private:
int value;
public:
test(int value) : value(value) {
std::cout << "test::test(int) : value = " << value << std::endl;
}
~test() {
std::cout << "test::~test() : value = " << value << std::endl;
}
int get_value() const { return value; }
void set_value(int new_value) { value = new_value; }
};
extern test global_test;
#endif // __BASE_HPP
e la fonte test.cpp
#include "base.hpp"
test global_test = test(1);
Poi ho scritto una libreria caricata dinamicamente: library.cpp
#include "base.hpp"
extern "C" {
test* get_global_test() { return &global_test; }
}
e un programma client caricato g questa libreria: client.cpp
#include <iostream>
#include <dlfcn.h>
#include "base.hpp"
typedef test* get_global_test_t();
int main() {
global_test.set_value(2); // global_test from libbase.a
std::cout << "client: " << global_test.get_value() << std::endl;
void* handle = dlopen("./liblibrary.so", RTLD_LAZY);
if (handle == NULL) {
std::cout << dlerror() << std::endl;
return 1;
}
get_global_test_t* get_global_test = NULL;
void* func = dlsym(handle, "get_global_test");
if (func == NULL) {
std::cout << dlerror() << std::endl;
return 1;
} else get_global_test = reinterpret_cast<get_global_test_t*>(func);
test* t = get_global_test(); // global_test from liblibrary.so
std::cout << "liblibrary.so: " << t->get_value() << std::endl;
std::cout << "client: " << global_test.get_value() << std::endl;
dlclose(handle);
return 0;
}
Ora compilare la libreria caricata staticamente con
g++ -Wall -g -c base.cpp
ar rcs libbase.a base.o
la libreria caricata dinamicamente
g++ -Wall -g -fPIC -shared library.cpp libbase.a -o liblibrary.so
e il client
g++ -Wall -g -ldl client.cpp libbase.a -o client
Ora osservo: il client e la libreria caricata dinamicamente possiedono una versione diversa della variabile global_test
. Ma nel mio progetto sto usando cmake. Lo script di build è simile al seguente:
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(globaltest)
ADD_LIBRARY(base STATIC base.cpp)
ADD_LIBRARY(library MODULE library.cpp)
TARGET_LINK_LIBRARIES(library base)
ADD_EXECUTABLE(client client.cpp)
TARGET_LINK_LIBRARIES(client base dl)
analizzando le creati makefile
s ho scoperto che CMake costruisce il cliente con
g++ -Wall -g -ldl -rdynamic client.cpp libbase.a -o client
Questo finisce in un comportamento leggermente diverso, ma fatale: La global_test
del cliente e la libreria caricata dinamicamente è la stessa, ma verrà distrutta due volte alla fine del programma.
Sto utilizzando cmake in modo errato? È possibile che il client e la libreria caricata dinamicamente utilizzino lo stesso global_test
ma senza questo doppio problema di distruzione?
La mia prima reazione sarebbe quella di mettere in discussione la necessità di questa variabile globale. –
Ok, nel mio programma originale questa variabile globale è una costante fornita da una libreria collegata staticamente. Tuttavia, verrà eliminato due volte nella versione cmake – phlipsy
Lo stesso problema si applicherebbe a qualsiasi modello singleton, quindi non vedo un problema con il globale – Elemental