Динамическая компиляция совместно используемой библиотеки с g++
Я пытаюсь скомпилировать следующий простой пример кода библиотеки DL из Program-Library-HOWTO с помощью g++. Это всего лишь пример, поэтому я могу научиться использовать и писать общие библиотеки. Настоящий код библиотеки, которую я разрабатываю, будет написан на C++.
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
cosine = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
printf ("%f\n", (*cosine)(2.0));
dlclose(handle);
}
Если я скомпилирую программу с помощью gcc, она будет работать нормально.
gcc -o foo foo.c -ldl
Когда я меняю имя файла и компилятор на следующий
g++ -o foo foo.cpp -ldl
Я получаю следующую ошибку:
foo.cpp: 16: ошибка: недопустимое преобразование из 'void*' в 'double (*)(double)'
Я понимаю (я думаю, я понимаю, поправьте меня, если это неправильно), что я не могу выполнить неявное приведение из пустого указателя в C++, но C позволяет мне, и именно поэтому приведенный выше код будет компилироваться с использованием gcc, но не с использованием г ++. Поэтому я попробовал явное приведение, изменив строку 16 выше:
cosine = (double *)dlsym(handle, "cos");
С этим на месте я получаю следующую ошибку:
foo.cpp: 16: ошибка: невозможно преобразовать 'double*' в 'double (*)(double)' в назначении
Эти проблемы, вероятно, больше связаны с моим собственным общим незнанием надлежащих стандартов кодирования C++, чем с чем-либо еще. Может кто-нибудь указать мне хороший учебник по разработке динамических библиотек для Linux, который использует пример кода C++?
4 ответа
C позволяет неявное приведение от void *
на любой тип указателя (включая указатели на функции); C++ требует явного приведения. Как говорит Лейфлундгрен, вам нужно привести приведенное значение dlsym()
к нужному типу указателя на функцию.
Многие люди находят синтаксис указателя на функцию C неудобным. Одним из распространенных шаблонов является определение типа указателя на функцию:
typedef double (*cosine_func_ptr)(double);
Вы можете определить свою переменную указателя функции cosine
как член вашего типа:
cosine_func_ptr cosine;
И приведение с использованием типа вместо неуклюжего синтаксиса указателя функции:
cosine = (cosine_func_ptr)dlsym(handle, "cos");
dlsym
возвращает указатель на символ. (Как void*
быть универсальным.) В вашем случае вы должны привести его к функции-указателю.
double (*mycosine)(double); // declare function pointer
mycosine = (double (*)(double)) dlsym(handle, "cos"); // cast to function pointer and assign
double one = mycosine(0.0); // cos(0)
Так что это один из тех редких случаев, когда ошибка компилятора является хорошей подсказкой.;)
В C++ вы должны выполнить reinterpret_cast
(не C-бросок):
typedef double (* double_from_double_function_t(double));
…
double_from_double_function_t cosine = reinterpret_cast<double_from_double_function_t>(dlsym(handle, "cos"));
То, как ваш код написан, на самом деле это больше вопрос C, но вы можете понять, как это будет работать в C++. У меня нет учебника для динамических общих библиотек (веб-страница, на которую вы ссылаетесь, кажется хорошей), но вот как исправить ваш код в C++:
объявите my_cos как функцию, которая будет (в конечном итоге) вызывать динамически загруженную функцию косинуса:
double my_cos(double);
назначить указатель на функцию my_cos
my_cos = (double (*)(double)) dlsym(handle, "cos");
Это немного сложно, но это присваивает my_cos что-то, что возвращает double, является результатом разыменования другого указателя функции и принимает double в качестве аргумента. Как написали другие люди, C++ требует большей ясности вашего кода, чем C.
замените это довольно устаревшее сообщение fputs на std::cerr или std::cout:
std::cerr << "error loading library cos: " << error << std::endl;
а также
std::cout << "result is " << (*my_cos)(2.0)) << std::endl;
Надеюсь, что это поможет. Если вас пугает эта странная вещь, я бы порекомендовал "Deep C Secrets" Ван Линдена и определенно "Книгу Кернигана и Ричи" на C.
Изменить: Хороший момент в комментарии о том, что вы специально ищете руководство по разработке на C++, а не C, чтобы избежать такого рода проблем. Я не знаю сравнимого руководства по C++, но около 99% кода C может быть встроено в код C++ и работать просто отлично. Этот случай указателя на функцию является одним из исключений.