Linux 上的 C++ 动态共享库

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/496664/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:56:27  来源:igfitidea点击:

C++ Dynamic Shared Library on Linux

c++linuxshared-libraries

提问by Bill the Lizard

This is a follow-up to Dynamic Shared Library compilation with g++.

这是使用 g++ 编译动态共享库的后续。

I'm trying to create a shared class library in C++ on Linux. I'm able to get the library to compile, and I can call some of the (non-class) functions using the tutorials that I found hereand here. My problems start when I try to use the classes that are defined in the library. The second tutorial that I linked to shows how to load the symbols for creating objects of the classes defined in the library, but stops short of usingthose objects to get any work done.

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

Does anyone know of a more complete tutorial for creating shared C++ class libraries that also shows how to usethose classes in a separate executable? A very simple tutorial that shows object creation, use (simple getters and setters would be fine), and deletion would be fantastic. A link or a reference to some open source code that illustrates the use of a shared class library would be equally good.

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



Although the answers from codelogicand nimrodmdo work, I just wanted to add that I picked up a copy of Beginning Linux Programmingsince asking this question, and its first chapter has example C code and good explanations for creating and using both static and shared libraries. These examples are available through Google Book Search in an older edition of that book.

尽管codelogicnimrodm的答案确实有效,但我只想补充一点,自从提出这个问题后,我拿起了一份Beginning Linux Programming的副本,它的第一章有示例 C 代码以及有关创建和使用静态库和共享库的良好解释. 这些示例可通过 Google 图书搜索在该书的较旧版本中找到

采纳答案by codelogic

myclass.h

我的类.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

myclass.cc

我的类.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

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

On Mac OS X, compile with:

在 Mac OS X 上,编译:

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

On Linux, compile with:

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

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

If this were for a plugin system, you would use MyClass as a base class and define all the required functions virtual. The plugin author would then derive from MyClass, override the virtuals and implement create_objectand destroy_object. Your main application would not need to be changed in any way.

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

回答by nimrodm

The following shows an example of a shared class library shared.[h,cpp] and a main.cpp module using the library. It's a very simple example and the makefile could be made much better. But it works and may help you:

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

shared.h defines the class:

shared.h 定义了类:

class myclass {
   int myx;

  public:

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

shared.cpp defines the getx/setx functions:

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

#include "shared.h"

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

main.cpp uses the class,

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

and the makefile that generates libshared.so and links main with the shared library:

以及生成 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

To actual run 'main' and link with libshared.so you will probably need to specify the load path (or put it in /usr/local/lib or similar).

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

The following specifies the current directory as the search path for libraries and runs main (bash syntax):

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

export LD_LIBRARY_PATH=.
./main

To see that the program is linked with libshared.so you can try ldd:

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

LD_LIBRARY_PATH=. ldd main

Prints on my machine:

在我的机器上打印:

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

回答by Matt Lewis

Basically, you should include the class' header file in the code where you want to use the class in the shared library. Then, when you link, use the '-l' flagto link your code with the shared library. Of course, this requires the .so to be where the OS can find it. See 3.5. Installing and Using a Shared Library

基本上,您应该在要使用共享库中的类的代码中包含类的头文件。然后,当您链接时,使用“-l”标志将您的代码与共享库链接。当然,这需要 .so 是操作系统可以找到它的地方。见3.5。安装和使用共享库

Using dlsym is for when you don't know at compile time which library you want to use. That doesn't sound like it's the case here. Maybe the confusion is that Windows calls the dynamically loaded libraries whether you do the linking at compile or run-time (with analogous methods)? If so, then you can think of dlsym as the equivalent of LoadLibrary.

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

If you really do need to dynamically load the libraries (i.e., they're plug-ins), then this FAQshould help.

如果您确实需要动态加载库(即它们是插件),那么这个 FAQ应该会有所帮助。

回答by Xavier Lamorlette

On top of previous answers, I'd like to raise awareness about the fact that you should use the RAII (Resource Acquisition Is Initialisation) idiomto be safe about handler destruction.

除了以前的答案之外,我想提高人们对以下事实的认识:您应该使用RAII(资源获取即初始化)习语来确保处理程序销毁的安全。

Here is a complete working example:

这是一个完整的工作示例:

Interface declaration: Interface.hpp:

接口声明Interface.hpp::

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

using Base_creator_t = Base *(*)();

Shared library content:

共享库内容:

#include "Interface.hpp"

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

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

Dynamic shared library handler: Derived_factory.hpp:

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

Client code:

客户端代码:

#include "Derived_factory.hpp"

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

Note:

笔记:

  • I put everything in header files for conciseness. In real life you should of course split your code between .hppand .cppfiles.
  • To simplify, I ignored the case where you want to handle a new/deleteoverload.
  • 为了简洁起见,我将所有内容都放在头文件中。在现实生活中,您当然应该在.hpp.cpp文件之间拆分代码。
  • 为简化起见,我忽略了您要处理new/delete重载的情况。

Two clear articles to get more details:

两篇清晰的文章以获得更多详细信息: