代码之家  ›  专栏  ›  技术社区  ›  Bill the Lizard

Linux上的C++动态共享库

  •  187
  • Bill the Lizard  · 技术社区  · 16 年前

    Dynamic Shared Library compilation with g++ .

    我试图在Linux上用C++创建一个共享类库。我能够编译库,并且可以使用我找到的教程调用一些(非类)函数 here here 。当我尝试使用库中定义的类时,我的问题就开始了。我链接到的第二个教程展示了如何加载符号以创建库中定义的类的对象,但缺少 使用

    有人知道一个更完整的创建共享C++类库的教程吗 使用 这些类在单独的可执行文件中?一个非常简单的教程,展示对象的创建、使用(简单的getter和setter就可以了)和删除将非常棒。一个链接或引用一些开源代码来说明共享类库的使用也同样好。


    codelogic nimrodm 做工作,我只是想补充一点,我拿起了一份 Beginning Linux Programming 自从提出这个问题以来,它的第一章有C代码示例,并对创建和使用静态库和共享库进行了很好的解释。这些例子可以通过谷歌图书搜索在 an older edition of that book .

    4 回复  |  直到 8 年前
        1
  •  170
  •   codelogic    16 年前

    myclass。英语字母表的第8个字母

    #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
    

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

    在Mac OS X上,使用以下命令编译:

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

    在Linux上,使用以下命令编译:

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

    如果这是一个插件系统,您将使用MyClass作为基类,并定义所有必需的函数virtual。然后,插件作者将从MyClass派生,覆盖虚值并实现 create_object destroy_object 。您的主应用程序不需要以任何方式进行更改。

        2
  •  55
  •   ashgkwd    12 年前

    下面显示了一个共享类库的示例。[h,cpp]和使用该库的main.cpp模块。这是一个非常简单的例子,makefile可以做得更好。但它确实有效,可能对你有所帮助:

    shared.h定义了类:

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

    shared.cpp定义了getx/setx函数:

    #include "shared.h"
    
    void myclass::setx(int newx) { myx = newx; }
    int  myclass::getx() { return myx; }
    

    main.cpp使用该类,

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

    以及生成libshared.so并将main与共享库链接的makefile:

    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
    

    要实际运行'main'并链接libshared.so,您可能需要指定加载路径(或将其放在/usr/local/lib或类似目录中)。

    下面指定当前目录作为库的搜索路径,并运行main(bash语法):

    export LD_LIBRARY_PATH=.
    ./main
    

    要查看程序是否与libshared链接,您可以尝试ldd:

    LD_LIBRARY_PATH=. ldd main
    

    在我的机器上打印:

      ~/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)
    
        3
  •  9
  •   Matt Lewis    16 年前

    除了之前的答案,我想提高人们对你应该使用 RAII (Resource Acquisition Is Initialisation) idiom 为了确保处理器销毁的安全。

    以下是一个完整的工作示例:

    接口声明: Interface.hpp :

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

    共享库内容:

    #include "Interface.hpp"
    
    class Derived: public Base {
    public:
        void foo() const override {}
    };
    
    extern "C" {
    Base * create() {
        return new Derived;
    }
    }
    

    动态共享库处理程序: 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);
            }
        }
    };
    

    客户代码:

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

    注:

    • 为了简洁起见,我把所有内容都放在头文件中。在现实生活中,你当然应该将代码拆分为 .hpp .cpp 文件夹。
    • 为了简化,我忽略了你想处理一个 new / delete 超载。

    两篇清晰的文章以获取更多细节:

        4
  •  7
  •   Xavier Lamorlette    6 年前

    基本上,您应该在要在共享库中使用类的代码中包含类的头文件。然后,当你链接时, use the '-l' flag 将您的代码与共享库链接。当然,这需要.so才能在操作系统中找到它。请参阅 3.5. Installing and Using a Shared Library

    当您在编译时不知道要使用哪个库时,可以使用dlsym。这听起来不像是这里的情况。也许令人困惑的是,无论是在编译时还是在运行时(使用类似的方法)进行链接,Windows都会调用动态加载的库?如果是这样,那么您可以将dlsym视为LoadLibrary的等价物。

    如果你真的需要动态加载库(即它们是插件),那么 this FAQ 应该有所帮助。