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

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

我希望用户select在运行时加载哪个共享库,而不用重新编译整个程序。

dlopen()是C的一个解决scheme,但是我的程序编写的是C ++ / Qt,提取的符号是Qt风格的类,有没有更多的“c ++”方法来做到这一点。

Solutions Collecting From Web of "C ++加载共享库并在Linux平台上运行时提取类实现"

你可以在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 )的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: %d\n", func()); } 

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