C++ Linux 平台上运行时加载共享库并提取类实现
C++ load shared library and extract class implementations at runtime on linux platform
在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版本。
除此之外,您还必须查找该符号的损坏版本。完成此操作后,可以使用与其匹配的函数/方法指针调用符号。根据设计,这不适用于构造函数/析构函数。如果要创建对象的新实例,则需要库提供的静态工厂方法。
如果库不提供工厂方法,则可以实现一个填充码库,该库通过泛型名称链接到目标库,并且确实提供工厂方法。您仍然需要通过函数/方法指针调用各个方法。
- 创建一个临时文件夹。
- 将填充程序库复制到临时文件夹。
- 将重命名为通用名称的目标库复制到临时文件夹中。
- 保存
LD_LIBRARY_PATH
环境变量的值。 - 将临时文件夹添加到
LD_LIBRARY_PATH
前面。 - 打开/加载库。
- 恢复
LD_LIBRARY_PATH
的保存值。
当然,您必须拥有库公开的任何接口的头文件。通常,仅给定动态库文件就无法重建它 - 主要是因为损坏的符号没有所用类型的完整结构信息。例如,即使你可以找到给定类的构造函数,你也不会知道类实例有多大(它的sizeof
)。
是的,在大多数操作系统上可以执行您所描述的操作,但是如何执行此操作取决于系统,无论系统如何,您肯定需要做更多的工作才能实现它。
一般步骤是:
- 加载库
- 对于库中您感兴趣的每个符号,找到它并存储到变量中以供以后使用。(这可以根据需要完成,而不是立即完成。
例如,在 *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 上执行此操作,则概念相同,但函数调用不同。
- 如果没有malloc,链表实现将失败
- 从包含m行的文件中提取n行,必要时(惰性地)重复该文件
- 如何在c++中实现处理器调度模拟器
- 如何在c++中使用引用实现类似python的行为
- 如何从 std::atomic 中提取指针 T<T>?
- 实现无开销push_back的最佳方法是什么
- 为什么istream不支持右值提取
- 使用简单类型列表实现的指数编译时间.为什么
- 如何在BST的这个简单递归实现中消除警告
- 实现一个在集合上迭代的模板函数
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 如何正确实现和访问运算符的各种自定义枚举器
- 如何实现递归"using"/提取模板参数
- 何时应该完全实现插入器/提取器
- 实现提取和插入运算符C++
- 最快的HOG特征提取实现
- 如何从实现(.cpp文件)中提取声明(.hpp文件)
- 将多个实现提取到静态库中
- 如何在类中实现提取操作符
- C++ Linux 平台上运行时加载共享库并提取类实现