动态加载,无需外部"C"
Dynamic Loading Without extern "C"
我一般想使用libdl来动态加载c++。这个问题是在运行时识别已命名混乱的符号。
正如这里所描述的,一种解决方案是通过使用extern "C"来消除名称混淆。
http://www.tldp.org/HOWTO/C + + dlopen/theproblem.html
这个解决方案的缺点是将动态加载的资源限制为C风格的接口。例如,动态加载的函数不能是重载函数。
克服这个限制的好方法是什么?
一种可能的解决方案是,当需要链接库时,使用工具对库源代码进行命名处理,并附带一个函数来获取修改后的名称。llvm是否为此提供了工具?
也许一个笨拙的解决方案是一个函数,它接受一个函数签名,用一个有签名的函数创建假代码,管道到编译器中,该编译器与一个用于生成汇编的标志一起使用,解析输出以检索经过修改的名称,并将经过修改的名称作为字符串返回。然后可以将字符串传递给dlsym()。
为了使问题具体化,这里有两个示例程序来说明外部"C"解决方案在不修改库代码的情况下无法动态加载的东西。第一个是用传统的c++方式动态链接一个库。第二种用法是"open"。在第一个程序中链接一个重载函数很简单。没有简单的方法来链接第二个程序中的重载函数。
程序1:Loadtime动态链接
main.cpp
// forward declarations of functions that will be linked
void say(int);
void say(float);
int main() {
int myint = 3;
say(myint);
float myfloat = 5.0f;
say(myfloat);
}
say.cpp
#include <iostream>
//extern "C" function signatures would collide
//extern "C" void say(int a) {
void say(int a) {
std::cout << "The int value is " << a << ".n";
}
//extern "C" void say(float a) {
void say(float r) {
std::cout << "The float value is " << r << ".n";
}
输出$ ./main
The int value is 3.
The float value is 5.
程序2:Runtime动态链接
main_with_dl.cpp
#include <iostream>
#include <dlfcn.h>
int main() {
// open library
void* handle = dlopen("./say_externC.so", RTLD_LAZY);
if (!handle) {
std::cerr << "dlopen error: " << dlerror() << 'n';
return 1;
}
// load symbol
typedef void (*say_t)(int);
// clear errors, find symbol, check errors
dlerror();
say_t say = (say_t) dlsym(handle, "say");
const char *dlsym_error = dlerror();
if (dlsym_error) {
std::cerr << "dlsym error: " << dlsym_error << 'n';
dlclose(handle);
return 1;
}
// use function
int myint = 3;
say(myint);
// can't load in void say(float)
// float myfloat = 5.0f;
// say(myfloat);
// close library
dlclose(handle);
}
输出$ ./main_with_dl
The int value is 3.
Makefile
CXX = g++
all: main main_with_dl say_externC.so
main: main.cpp say.so
$(CXX) -o $@ $^
main_with_dl: main_with_dl.cpp
$(CXX) -o $@ $<
%.so : %.cpp
$(CXX) -shared -o $@ $<
.PHONY: clean
clean:
rm main main_with_dl say.so say_externC.so
多亏了Mooing Duck,我才能够在Visual Studio的启发下使用clang想出一个解决方案。
键是由Visual Studio和clang提供的一个宏。__FUNCDNAME__
宏解析为封闭函数的混乱名称。通过定义与我们想要动态链接的函数具有相同签名的函数,我们可以使__FUNCDNAME__
解析到所需的名称manggle。
这是程序2的新版本,它可以调用void say(int)
和void say(float)
。
EDIT moving Duck给了我更多的知识。这是main_with_dl.cpp
的一个版本,在问题中与say.cpp
一起工作。
#include <iostream>
#include <dlfcn.h>
void* handle;
template<class func_sig> func_sig get_func(const char* signature)
{
dlerror();
func_sig func = (func_sig) dlsym(handle, signature);
const char *dlsym_error = dlerror();
if (dlsym_error) {
std::cerr << "dlsym error: " << dlsym_error << 'n';
dlclose(handle);
exit(1);
}
return func;
}
void say(int a) {
typedef void(*func_sig)(int);
static func_sig func = get_func<func_sig>(__FUNCDNAME__);
return func(a);
}
void say(float a) {
typedef void(*func_sig)(float);
static func_sig func = get_func<func_sig>(__FUNCDNAME__);
return func(a);
}
int main() {
// open library
//void* handle = dlopen("./say_externC.so", RTLD_LAZY);
handle = dlopen("./say.so", RTLD_LAZY);
if (!handle) {
std::cerr << "dlopen error: " << dlerror() << 'n';
return 1;
}
// use function
int myint = 3;
say(myint);
float myfloat = 5.0f;
say(myfloat);
// close library
dlclose(handle);
}
http://coliru.stacked-crooked.com/a/7249cc6c82ceab00 代码必须使用clang++
和-fms-extensions
标志进行编译,__FUNCDNAME__
才能工作
- std::原子加载和存储都需要吗
- 加载由 MATLAB Coder 生成的带有函数的 DLL,该函数调用外部函数
- 加载共享库时出错:无法在外部硬件上打开共享对象文件:
- 在模块加载/卸载时构造/破坏外部c++模块中的对象
- PostgreSQL C-Extension加载另一个外部库
- QtCreator:正在加载外部库
- 如何将值加载到外部共享数组中
- 如何在不使用外部库的情况下加载到 8 位C++位图图像
- SDL2 在函数外部加载字体会引发错误
- 通过HTTP加载外部HTML时DHTML_EVENT_ONCLICK不起作用?
- 加载外部共享库时出现 G++ 链接问题
- 在Windows中的Qt中加载外部dll
- 错误LNK2001:加载的静态库的未解析外部符号
- Xcode 5 C++SDL应用程序未在Xcode IDE外部加载
- 将外部dll项目用于Excel的c++/xll加载项
- 在Qt中加载外部样式表
- 从外部调用封装类成员函数时不加载SFML纹理
- 加载外部库时遇到麻烦
- 动态加载,无需外部"C"
- 如何为Apache2模块加载依赖项(外部库)