Компиляция динамической общей библиотеки с g++
Я пытаюсь скомпилировать следующий простой пример библиотеки DL библиотеки из Program-Library-HOWTO с g++. Это просто пример, поэтому я могу научиться использовать и писать разделяемые библиотеки. Настоящий код для библиотеки, которую я разрабатываю, будет написан на С++.
#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)'
Я понимаю (я думаю, что понимаю, исправьте меня, если это не так), что я не могу сделать неявный приведение из указателя void в С++, но C позволяет мне, и поэтому приведенный выше код будет скомпилирован с использованием gcc но не используя g++. Поэтому я попробовал явное преобразование, изменив строку 16 выше:
cosine = (double *)dlsym(handle, "cos");
При этом я получаю следующую ошибку:
foo.cpp: 16: error: невозможно преобразовать 'double *' в 'double (*) (double)' в присваивании
Эти проблемы, вероятно, больше связаны с моим собственным общим незнанием надлежащих стандартов кодирования на С++, чем с чем-либо еще. Может ли кто-нибудь указать мне хороший учебник по разработке динамических библиотек для Linux, который использует код примера С++?
Ответы
Ответ 1
C допускает неявные приведения от void *
к любому типу указателя (включая указатели на функции); С++ требует явного литья. Как говорит leiflundgren, вам нужно вернуть возвращаемое значение dlsym()
к типу указателя функции, который вам нужен.
Многие люди считают синтаксис указателя функции C неудобным. Один общий шаблон - typedef указатель функции:
typedef double (*cosine_func_ptr)(double);
Вы можете определить переменную указателя функции cosine
в качестве члена вашего типа:
cosine_func_ptr cosine;
И применяйте этот тип вместо синтаксиса указателя неудобной функции:
cosine = (cosine_func_ptr)dlsym(handle, "cos");
Ответ 2
dlsym
возвращает указатель на символ. (As 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)
Итак, этот один из этих редких случаев, когда ошибка компилятора является хорошим ключом.;)
Ответ 3
С учетом вашего кода, если он написан, это скорее вопрос C, но вы можете получить это, чтобы он работал на С++. У меня нет учебника для вас в динамических общих библиотеках (веб-страница, с которой вы связаны, кажется прекрасной), но вот как исправить свой код на С++:
-
объявить my_cos функцией, которая (в конечном счете) вызовет динамически загруженную функцию косинуса:
double my_cos(double);
-
назначить указатель на функцию my_cos
my_cos = (double (*)(double)) dlsym(handle, "cos");
Это немного сложно, но присваивание my_cos тому, что возвращает double, является результатом разыменования другого указателя функции и принимает двойной аргумент. Как и другие люди, С++ немного более требовательна к объяснению вашего кода, чем C.
и
std::cout << "result is " << (*my_cos)(2.0)) << std::endl;
Надеюсь, что эта помощь. Если этот странный кастовый материал пугает вас, я бы порекомендовал Deep C Secrets ван Линден и, безусловно, книгу Кернигана и Ричи на C.
Изменить: Хорошая точка в комментарии о том, как вы ищете руководство по разработке на С++, а не C, чтобы избежать этой проблемы. Я не знаю сравнительного руководства на С++, но около 99% кода C можно встроить в код С++ и работать просто отлично. Этот случай указателя функции является одним из исключений.