Динамическая компиляция совместно используемой библиотеки с 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++ и работать просто отлично. Этот случай указателя на функцию является одним из исключений.

Другие вопросы по тегам