C++ Linux C++;动态Libs和静态初始化顺序

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

请原谅我的长篇大论。这是一个复杂的问题,我需要一个完整的描述

在LinuxFedora21(g++4.9.2)等平台上

我正在使用一个“数据库”基类和继承的Oracle和Sybase .</P>类来处理C++数据库包装器库。 除此之外,我希望一些程序能够在运行时使用dlopen动态加载库,遵循和其他程序的模板

但是,其他程序可以直接链接所需的版本。我的解决方案的问题是database.cpp中存在一个静态std::map(请参见下面的代码),必须先对其进行初始化,然后才能将其分配给oracle/sybase.cpp的静态初始值设定项。我能够做到这一点的唯一方法是通过编译时-l参数的物理顺序(参见Makefile)使它们正确:,程序运行正常将它们向后移动:--程序编译和链接会找到,但执行时会立即崩溃。这让我很烦恼,因为我更喜欢一个成功的编译/链接来产生一个成功的运行,而库顺序的一个合理的翻转通常不能做到这一点。我知道链接顺序导致链接错误并不罕见,但运行时错误并不罕见

两个问题:
  • 除了库顺序外,是否还有其他链接选项可用于确保正确初始化
  • 有人能看到另一种算法来消除这种依赖性吗。(我不希望普通程序必须声明DBFactory,它需要保留在库中,但在main_dlopen中多做一步就可以了)
  • 下面是代码、所有示例模块和Makefile。包括两个主程序,一个用于正常链接(main_direct),一个用于运行时链接(main_dlopen)。两者都将按照给定的方式编译和运行(除非有剪切粘贴错误)

    谢谢你的关注

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