使用g++进行动态共享库编译

17
我正在尝试使用g++编译来自 Program-Library-HOWTO 的以下简单DL库示例代码。这只是一个示例,所以我可以学习如何使用和编写共享库。我要开发的库的真实代码将用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: error: invalid conversion from 'void*' to 'double (*)(double)'

我理解(如果我错了请指出)C++中不能从void指针进行隐式转换,但是在C语言中可以。这就是为什么上述代码在使用gcc编译时会成功,但在使用g++编译时不会成功的原因。因此,我尝试通过将第16行代码更改为以下内容来显式转换:

cosine = (double *)dlsym(handle, "cos");

在这种情况下,我收到以下错误:

foo.cpp:16: 错误:无法将“double *”转换为“double (*)(double)”

这些问题可能更多地与我自己对正确的C++编码标准的无知有关。是否有人可以指点我一份在Linux上开发动态库并使用C++示例代码的好教程?


我建议你阅读《C++ 编程思想》来快速掌握 C++。正如你所指出的,C++ 中不能将 void* 隐式转换为其他指针类型,这意味着你需要先掌握 C++ 而不是其他领域 :) - workmad3
谢谢,我在业余时间正在阅读C++ Primer,但现在我有一个项目要交。 - Bill the Lizard
4个回答

27

C允许从void *到任何指针类型(包括函数指针)的隐式转换;C++要求显式转换。正如leiflundgren所说,您需要将dlsym()的返回值强制转换为所需的函数指针类型。

许多人觉得C的函数指针语法很笨拙。一种常见的模式是typedef函数指针:

typedef double (*cosine_func_ptr)(double);

你可以将函数指针变量cosine定义为你的类型的成员:

cosine_func_ptr cosine;

使用类型转换而不是笨拙的函数指针语法进行强制转换:

cosine = (cosine_func_ptr)dlsym(handle, "cos");

我不禁想,你一定读了我的心才能找出我的真正问题。谢谢。 :) - Bill the Lizard
7
请使用reinterpret_cast<cosine_func_ptr>()而不是C风格的强制类型转换操作符。 - Martin York

9

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)

这是一种罕见的情况,编译器错误是一个很好的提示。;)


这个可以在原始代码中使用 double (*cosine)(double);,但不能用你提供的函数指针声明。由于我是这里提问的无知之人,所以我不觉得应该改变你的代码。 :) - Bill the Lizard

0

根据你的代码编写方式,这更像是一个C语言问题,但你可以让它在C++中运行。我没有关于动态共享库的教程(你提供的网页看起来很好),但以下是如何在C++中修复你的代码:

  • 声明my_cos为一个函数,该函数最终将调用动态加载的余弦函数:
  • double my_cos(double);
    
  • 将函数指针指向my_cos:
  • my_cos = (double (*)(double)) dlsym(handle, "cos");
    

这有点复杂,但它将一个返回双精度的函数指针解引用的结果分配给了my_cos,并且需要一个双精度参数。正如其他人所发表的,C++对代码的明确性要求比C更高。

用 std::cerr 或 std::cout 替换那个有点过时的 fputs 消息:
std::cerr << "error loading library cos: " << error << std::endl;

并且

std::cout << "result is " << (*my_cos)(2.0)) << std::endl;

希望这可以帮到你。如果奇怪的类型转换让你感到害怕,我建议你阅读 van Linden 的《深入C语言》和 Kernighan 以及 Ritchie 的《C语言程序设计》。
编辑:评论中提到了一个很好的观点,即您特别寻找 C++ 开发指南而非 C,以避免出现这种问题。我不知道有没有类似的 C++ 指南,但是大约有 99% 的 C 代码可以嵌入到 C++ 代码中并正常工作。函数指针是其中之一的例外情况。

提供的示例代码是C语言,但我将使用C ++进行开发,因此需要使用g++编译。这个问题可能很幼稚,答案可能是“不要使用g++编译C语言代码”。 :) - Bill the Lizard

0
在C++中,你必须执行一个reinterpret_cast(而不是C cast):
typedef double (* double_from_double_function_t(double));
…
double_from_double_function_t cosine = reinterpret_cast<double_from_double_function_t>(dlsym(handle, "cos"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接