Я хотел бы использовать libdl для динамической загрузки C ++ в целом. Проблема заключается в идентификации символов во время выполнения, которые были искажены по имени.
Как описано здесь, одним из решений является удаление искажения имени с помощью внешнего «C».
http://www.tldp.org/HOWTO/C++-dlopen/theproblem.html
Это решение имеет недостаток, заключающийся в ограничении динамически загружаемых ресурсов интерфейсами в стиле C. Динамически загруженные функции не могут, например, быть перегруженными функциями.
Какой хороший способ преодолеть это ограничение?
Одним из возможных решений было бы использование инструментов для именования исходного кода библиотеки с помощью сопровождающей функции для получения искаженных имен, когда библиотека должна быть связана. Предоставляет ли llvm инструменты для этого?
Возможно, неуклюжим решением будет функция, которая принимает сигнатуру функции, создает фиктивный код с функцией, которая имеет сигнатуру, передает в компилятор, который использовался с флагом для генерации сборки, анализирует выходные данные для получения искаженного имени и возвращает искаженное имя в виде строки. Строка может быть передана в dlsym ().
Чтобы конкретизировать проблему, вот две примеры программ, которые иллюстрируют то, что внешнее решение «C» не может динамически загрузить без изменения библиотечного кода. Первый динамически связывает библиотеку традиционным способом C ++. Второй использует длопен. Связать перегруженную функцию в первой программе просто. Нет простого способа связать перегруженную функцию во второй программе.
Программа 1: Динамическое связывание во время загрузки
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: Динамическое связывание во время выполнения
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 я смог придумать решение, используя clang и вдохновленный Visual Studio.
Ключ — это макрос, предоставляемый Visual Studio и clang. Макрос __FUNCDNAME__ преобразуется в искаженное имя включающей функции. Определив функции с той же сигнатурой, что и те, которые мы хотим динамически связать, мы можем получить __FUNCDNAME__ для разрешения нужного имени mangle.
Вот новая версия программы 2, которая может вызывать как void say (int), так и void say (float).
РЕДАКТИРОВАТЬ Мытье утки дало мне больше знаний. Вот версия main_with_dl.cpp, которая работает с say.cpp в вопросе.
http://coliru.stacked-crooked.com/a/7249cc6c82ceab00
Код должен быть скомпилирован с использованием clang ++ с флагом -fms-extensions для работы __FUNCDNAME__.