C++在Linux平台运行时加载共享库并提取类实现
Posted
技术标签:
【中文标题】C++在Linux平台运行时加载共享库并提取类实现【英文标题】:C++ load shared library and extract class implementations at runtime on linux platform 【发布时间】:2014-04-01 19:24:22 【问题描述】:在 C++ 中,是否可以在执行时加载共享库?
我希望用户在运行时选择要加载的共享库,而不需要重新编译整个程序。
dlopen()
是C的解决方案,但我的程序是用C++/Qt编写的,提取的符号是Qt风格的类,有没有更“c++”的方式来做到这一点。
【问题讨论】:
这是什么平台? 是的,这是可能的,但与c++ 或qt 没有本质上的关系,它取决于操作系统如何在运行时将共享库加载到您的程序中。从您的代码中运行它有什么特别的问题? 我想从lib中提取的是qt类,我不知道在qt中有没有这样的机制。 【参考方案1】:您可以在 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;
【讨论】:
我已将共享库的代码添加到答案中。如您所见,外部“C”使您可以动态访问 C++ 类。 “sum”的成员函数的“虚”声明是必须的吗? 必须是虚拟的。因为我们声明了它,在使用的时候没有定义它。 非常感谢,我也发现无法从类中检索静态成员【参考方案2】:如果目标库本身,或者至少它的规范在您的控制之下,那么您不应该使用QLibrary
- 而是使用Qt plugin system。它不需要其他需要的通过指针调用体操。
如果你坚持使用类似dlopen
的机制,那么QLibrary
就没有C 特有的东西。明显的限制是,您尝试打开的库必须使用与用于编译您自己的代码的 ABI 兼容的 C++ 编译器进行编译。在 Windows 上,这实际上意味着使用相同的 MSVC 版本。
除此之外,您还必须look up the mangled version of the symbol。完成此操作后,您可以使用与其匹配的函数/方法指针调用该符号。这个won't work on constructors/destructors,设计使然。如果您希望创建新的对象实例,则需要库提供的静态工厂方法。
如果库不提供工厂方法,您可以实现一个 shim 库,该库通过通用名称链接到目标库并提供工厂方法。您仍然需要通过函数/方法指针调用各个方法。
-
创建一个临时文件夹。
将 shim 库复制到临时文件夹。
将重命名为通用名称的目标库复制到临时文件夹中。
保存
LD_LIBRARY_PATH
环境变量的值。
将临时文件夹添加到LD_LIBRARY_PATH
。
打开/加载库。
恢复LD_LIBRARY_PATH
的保存值。
当然,对于库公开的任何接口,您都必须拥有头文件。通常,仅给定一个动态库文件就无法对其进行重构-主要是因为损坏的符号没有所用类型的完整结构信息。例如,即使您可以找到给定类的构造函数,您也不知道该类实例有多大(它的sizeof
)。
【讨论】:
【参考方案3】:是的,可以在大多数操作系统上执行您所描述的操作,但是您如何执行取决于系统,并且无论系统如何,您都需要做更多的工作才能实现它。
一般步骤是:
-
加载库
对于库中您感兴趣的每个符号,找到它并将其存储到一个变量中以供以后使用。 (这可以根据需要完成,而不是立即完成。)
例如,在 *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 上执行此操作,则概念相同,但函数调用不同。
【讨论】:
以上是关于C++在Linux平台运行时加载共享库并提取类实现的主要内容,如果未能解决你的问题,请参考以下文章