Компиляция динамической общей библиотеки с 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.

  • замените это скорее датированное сообщение 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, чтобы избежать этой проблемы. Я не знаю сравнительного руководства на С++, но около 99% кода C можно встроить в код С++ и работать просто отлично. Этот случай указателя функции является одним из исключений.