gpt4 book ai didi

c++ - 没有外部的动态加载 "C"

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:44:11 26 4
gpt4 key购买 nike

一般情况下,我想使用 libdl 动态加载 C++。问题是在运行时识别已被名称损坏的符号。

如此处所述,一种解决方案是使用 extern "C"删除名称重整。

http://www.tldp.org/HOWTO/C++-dlopen/theproblem.html

此解决方案的缺点是将动态加载的资源限制为 C 风格的接口(interface)。例如,动态加载的函数不能是重载函数。

克服这个限制的好方法是什么?

一种可能的解决方案是使用工具对库源代码进行命名修改,并附带一个函数,以便在需要链接库时获取修改后的名称。 llvm 是否为此提供工具?

也许一个笨拙的解决方案是一个函数,它接受一个函数签名,用一个有签名的函数创建伪代码,通过管道传输到编译器中,编译器使用一个标志来生成程序集,解析输出以检索损坏的名称, 并以字符串形式返回损坏的名称。然后可以将该字符串传递给 dlsym()。

为了使问题具体化,这里有两个示例程序说明了 extern "C"解决方案在不修改库代码的情况下无法动态加载的内容。第一个以传统的 C++ 方式动态链接库。第二个使用 dlopen。在第一个程序中链接重载函数很简单。没有简单的方法来链接第二个程序中的重载函数。

程序一:加载时动态链接

主要.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);
}

说.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.

方案二:运行时动态链接

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.

编译

生成文件

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,我能够使用 clang 并受到 Visual Studio 的启发提出一个解决方案。

关键是Visual Studio和clang提供的宏。 _FUNCDNAME_ 宏解析为封闭函数的损坏名称。通过定义与我们想要动态链接的函数具有相同签名的函数,我们可以获得 _FUNCDNAME_ 来解析为所需的名称 mangle。

这里是程序 2 的新版本,它可以同时调用 void say(int) 和 void say(float)。

编辑 Mooing Duck 给我带来了更多知识。这是与问题中的 say.cpp 一起使用的 main_with_dl.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

代码必须使用带有 -fms-extensions 标志的 clang++ 编译,才能让 _FUNCDNAME_ 工作。

关于c++ - 没有外部的动态加载 "C",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24088464/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com