C++ Linux C++;动态Libs和静态初始化顺序
请原谅我的长篇大论。这是一个复杂的问题,我需要一个完整的描述 在LinuxFedora21(g++4.9.2)等平台上 我正在使用一个“数据库”基类和继承的Oracle和Sybase .</P>类来处理C++数据库包装器库。 除此之外,我希望一些程序能够在运行时使用dlopen动态加载库,遵循和其他程序的模板 但是,其他程序可以直接链接所需的版本。我的解决方案的问题是database.cpp中存在一个静态std::map(请参见下面的代码),必须先对其进行初始化,然后才能将其分配给oracle/sybase.cpp的静态初始值设定项。我能够做到这一点的唯一方法是通过编译时-l参数的物理顺序(参见Makefile)使它们正确:,程序运行正常将它们向后移动:--程序编译和链接会找到,但执行时会立即崩溃。这让我很烦恼,因为我更喜欢一个成功的编译/链接来产生一个成功的运行,而库顺序的一个合理的翻转通常不能做到这一点。我知道链接顺序导致链接错误并不罕见,但运行时错误并不罕见 两个问题:C++ Linux C++;动态Libs和静态初始化顺序,c++,linux,dynamic,initialization,shared-libraries,C++,Linux,Dynamic,Initialization,Shared Libraries,请原谅我的长篇大论。这是一个复杂的问题,我需要一个完整的描述 在LinuxFedora21(g++4.9.2)等平台上 我正在使用一个“数据库”基类和继承的Oracle和Sybase .类来处理C++数据库包装器库。 除此之外,我希望一些程序能够在运行时使用dlopen动态加载库,遵循和其他程序的模板 但是,其他程序可以直接链接所需的版本。我的解决方案的问题是database.cpp中存在一个静态std::map(请参见下面的代码),必须先对其进行初始化,然后才能将其分配给oracle/syba
// database.h
#include <map>
#include <string>
#include <iostream>
#include <stdio.h>
class Database; // forward declaration
typedef Database* maker_t();
// our global factory
typedef std::map<std::string, maker_t *> DBFactory_t;
extern DBFactory_t DBFactory;
class Database
{
public:
Database () {}
virtual ~Database () {};
};
//main\dlu open.cpp
#包括
#包括
#包括
#包括“database.h”
使用名称空间std;
int main()
{
void*dl=dlopen(“libSybase.so”,RTLD_NOW);
如果(!dl){cerr这是一个相当标准的问题:您有全局数据,无法控制它何时初始化
这个问题还有一个标准的解决方案:通过函数调用间接初始化数据
不要使用全局std::map DBFactory
,而是执行以下操作:
// database.cpp
DBFactory_t& getDBFactory() {
static DBFactory_t factory;
return factory;
}
// oracle.cpp
proxy ()
{
// register the maker with the factory
fprintf (stderr, "Oracle Proxy Constructor\n");
getDBFactory()["ORACLE"] = Maker;
}
瞧:工厂将在您第一次需要时建造
您还有一个问题,您还没有发现:如果将两个文件加载到单个进程中,oracle.cpp
和sybase.cpp
定义函数Maker
,class proxy
和变量p
,则会导致ODR冲突和未定义的行为。您最好使用se将名称空间分开以避免出现这种情况。这样做了。这是一个比我想象的更简单的解决方案。非常感谢。你是对的,这是处理静态初始化顺序问题的最简单解决方案。尽管这是一个更简单的解决方案,但可能无法跨多个线程(两个线程同时调用工厂)。在初始化之前,您需要一个互斥锁,如果您多次调用工厂,这可能会使您付出代价。@Hans初始化函数静态对象是通过(我相信)保证线程安全的C++11,不需要锁。g++
在2011年之前就提供了这一保证,但如果您的编译器不符合C++11,并且您正在进行多线程编程,那么您确实需要一个锁。@EmployedRussian是的,您是正确的。C++11及以上版本保证这是线程安全的
// oracle.h
#include "database.h"
class Oracle : public Database
{
public:
Oracle ();
virtual ~Oracle ();
};
// oracle.cpp class.
#include "oracle.h"
using namespace std;
__attribute__((constructor)) void fooOracle (void) {
fprintf (stderr, "Oracle library loaded\n");
}
// the following code follows the referece at
// http://www.linuxjournal.com/article/3687?page=0,0
extern "C" {
Database * Maker()
{
return new Oracle;
}
class proxy {
public:
proxy ()
{
// register the maker with the factory
fprintf (stderr, "Oracle Proxy Constructor\n");
DBFactory["ORACLE"] = Maker;
}
};
}
proxy p;
Oracle::Oracle () {
cout << "Oracle Constructor" << endl;
}
Oracle::~Oracle ()
{
cout << "Oracle Destructor" << endl;
}
// sybase.h
#include "database.h"
class Sybase : public Database
{
public:
Sybase ();
virtual ~Sybase();
};
// sybase.cpp class.
#include "sybase.h"
using namespace std;
__attribute__((constructor)) void fooSybase (void) {
fprintf (stderr, "Sybase library loaded\n");
}
extern "C" {
Database * Maker()
{
return new Sybase;
}
class proxy {
public:
proxy ()
{
// register the maker with the factory
fprintf (stderr, "Sybase Proxy Constructor\n");
DBFactory["SYBASE"] = Maker;
}
};
}
proxy p;
Sybase::Sybase () {
cout << "Sybase Constructor" << endl;
}
Sybase::~Sybase ()
{
cout << "Sybase Destructor" << endl;
}
// main_direct.cpp
#include "oracle.h"
int main ()
{
Oracle db ();
return 0;
}
// main_dlopen.cpp
#include <iostream>
#include <dlfcn.h>
#include <stdlib.h>
#include "database.h"
using namespace std;
int main ()
{
void * dl = dlopen ("libSybase.so", RTLD_NOW);
if (!dl) { cerr << dlerror() << endl; exit (1); }
Database * db = DBFactory["SYBASE"] ();
delete db;
return 0;
}
#Makefile
CXXFLAGS = -Wall -fPIC -ggdb -std=c++11
all: main libOracle.so libSybase.so libdb.so
main: main_dlopen main_direct
main_dlopen: main_dlopen.o libdb.so libOracle.so libSybase.so
${CXX} -o main_dlopen main_dlopen.o -L. -ldb -ldl
# reverse -lOracle and -ldb to make this program crash
main_direct: main_direct.o libdb.so libOracle.so libSybase.so
${CXX} -o main_direct main_direct.o -L. -lOracle -ldb
libOracle.so: oracle.o
${CXX} -shared -fPIC -o libOracle.so oracle.o
libSybase.so: sybase.o
${CXX} -shared -fPIC -o libSybase.so sybase.o
libdb.so: database.o
${CXX} -shared -fPIC -o libdb.so database.o
clean:
rm -f *.o *.so main_dlopen main_direct
%.o : %.cpp
${CXX} ${CXXFLAGS} -c $< -o $@
// database.cpp
DBFactory_t& getDBFactory() {
static DBFactory_t factory;
return factory;
}
// oracle.cpp
proxy ()
{
// register the maker with the factory
fprintf (stderr, "Oracle Proxy Constructor\n");
getDBFactory()["ORACLE"] = Maker;
}