C++ Linux 平台上运行时加载共享库并提取类实现

C++ load shared library and extract class implementations at runtime on linux platform

本文关键字:提取 实现 共享 加载 Linux 平台 运行时 C++      更新时间:2023-10-16

在C++中,是否可以在执行时加载共享库?

我希望用户选择要在运行时加载的共享库,而无需重新编译整个程序。

dlopen()是C的解决方案,但我编写的程序是C++/Qt,要提取的符号是Qt风格的类,有没有更"C ++"的方法可以做到这一点。

你可以在

Qt中使用QLibrary通过两种方式做到这一点。下面的示例在运行时以两种不同的方式调用共享库中的函数:

#include <QLibrary>
#include <QDebug>
class  Dynamic_library
{
public:
    Dynamic_library();
    virtual int sum( int len, int * data );
};
typedef Dynamic_library * (*get_object_func)();
typedef int (*call_sum_func)(int len , int * data);
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QLibrary library( "./dynamic_library" );
    library.load();
    if( !library.isLoaded() )
    {
        qDebug() << "Cannot load library.";
        return 0;
    }
    call_sum_func call_sum = (call_sum_func)library.resolve( "call_sum" );
    if( call_sum )
    {
        //Dynamic_library * obj=get_object();
        int * a=new int[3];
        a[0]=2;
        a[1]=3;
        a[2]=4;
        qDebug() << "sum of 2+3+4' = " << call_sum( 3, a ) <<"n";
        delete [] a;
    }
    get_object_func get_object = (get_object_func)library.resolve( "get_object" );
    if( get_object )
    {
        Dynamic_library * obj=get_object();
        int * a=new int[3];
        a[0]=7;
        a[1]=8;
        a[2]=9;
        qDebug() << "sum of 7+8+9' = " << obj->sum(3, a );
        delete [] a;
    }
    return a.exec();
}

共享库的代码如下所示:

class DYNAMIC_LIBRARYSHARED_EXPORT Dynamic_library
{
public:
    Dynamic_library();
    virtual int sum( int len, int * data );
};
extern "C" Q_DECL_EXPORT Dynamic_library * get_object()
{
     return new Dynamic_library();
}
extern "C" Q_DECL_EXPORT int call_sum(int len, int * data)
{
     return Dynamic_library().sum(len,data);
}

Dynamic_library::Dynamic_library()
{
}
int Dynamic_library::sum( int len, int *data )
{
    int sum = 0;
    for(int i=0; i<len; ++i )
        sum += data[i];
    return sum;
}

如果目标库本身,或者至少它的规范,在你的控制之下,那么你不应该使用QLibrary - 改用Qt插件系统。它不需要其他需要的呼叫指针体操。

如果你坚持使用类似dlopen的机制,那么QLibrary就没有什么特定于C的。明显的限制是,您尝试打开的库必须使用与您用于编译自己的代码的编译器兼容的 C++ABI 编译器编译。在Windows上,这实际上意味着使用相同的MSVC版本。

除此之外,您还必须查找该符号的损坏版本。完成此操作后,可以使用与其匹配的函数/方法指针调用符号。根据设计,这不适用于构造函数/析构函数。如果要创建对象的新实例,则需要库提供的静态工厂方法。

如果库不提供工厂方法,

则可以实现一个填充码库,该库通过泛型名称链接到目标库,并且确实提供工厂方法。您仍然需要通过函数/方法指针调用各个方法。

  1. 创建一个临时文件夹。
  2. 将填充程序库复制到临时文件夹。
  3. 将重命名为通用名称的目标库复制到临时文件夹中。
  4. 保存LD_LIBRARY_PATH环境变量的值。
  5. 将临时文件夹添加到 LD_LIBRARY_PATH 前面。
  6. 打开/加载库。
  7. 恢复LD_LIBRARY_PATH的保存值。

当然,您必须拥有库公开的任何接口的头文件。通常,仅给定动态库文件就无法重建它 - 主要是因为损坏的符号没有所用类型的完整结构信息。例如,即使你可以找到给定类的构造函数,你也不会知道类实例有多大(它的sizeof)。

是的,在大多数操作系统上可以执行您所描述的操作,但是如何执行此操作取决于系统,无论系统如何,您肯定需要做更多的工作才能实现它。

一般步骤是:

  1. 加载库
  2. 对于库中您感兴趣的每个符号,找到它并存储到变量中以供以后使用。(这可以根据需要完成,而不是立即完成。

例如,在 *nix 类型系统上的伪代码(阅读:这不会编译!)中,让我们假设您的共享库中包含以下内容:

// I'm declaring this _extern "C"_ to avoid name mangling, making it easier to
// specify a symbol name for dlsym() later
extern "C" int myFunction() {
    return 10;
}

假设这是在一个名为 libmyFunction.so 的库中。例如,您的主应用程序可以:

{
    void *handle = dlopen("libmyFunction.so", <flags>);
    if (!handle) return; // error: cannot locate the library!
    int (*func)() = (int (*)())dlsym(handle, "myFunction");
    if (!func) return; // error: cannot locate the symbol!
    printf("The function returns: %dn", func());
}

如果您需要在 Windows 上执行此操作,则概念相同,但函数调用不同。