7
4

4 回答 4

1

首先,g++将链接视为类型的一部分。你不so.hpp 应该编译,因为它试图用 初始化一个myclass* (*)() (指向extern "C"函数的指针)make,这是一个extern "C"函数。这是非法的,并且需要编译器错误,但 g++ 接受它甚至没有警告。

除此之外,如果未定义,为什么要host.cpp生成警告。usefunction在 DLL 中,interface包含指向函数的指针的数据类型的实例。您用于dlsym获取此变量(不是函数)的地址,并将其转换为数据类型。永远不要将指向数据的指针转换为指向函数的指针;您取消引用指向包含指向函数的指针的数据对象的指针,这很好。

至于带有. reinterpret_cast_ 标准(至少通过 C++03)说这种转换是非法的,并且我使用了无法使其工作的编译器,因为指向函数的指针大于指向数据的指针。作为在 C 中允许的限制,Unix (Posix) 要求指向函数的指针和指向数据的指针具有相同的大小和表示形式,并且 Posix 标准规定将返回值转换如下:dlsymvoid*dlsym

myclass* (*func)();
*reinterpret_cast<void**>( &func ) = dlsym( th, "make" );

如果 amyclass* (*)()和 avoid*实际上具有相同的大小和表示,这是合法的,并且会起作用(并且不应触发任何警告)。

于 2012-09-25T14:49:05.733 回答
1

gcc 的警告是因为 gcc 不知道您是否愿意假设 Posix。所以它假设不是并且(根据 C++ 标准的要求)它诊断出格式错误的程序。

但是,您正在使用dlsym并期望它完成 Posixdlsym所做的事情,因此您愿意依赖 Posix。然后你可以对你的函数指针类型进行 C 风格的void*转换,并且 gcc 保证这是可以的,即使 C++ 没有。任何模仿的非 Posix 系统dlsym都必须保证类似的东西,否则在 a 中返回函数指针是无意义的void*

既然您知道自己在做什么,那么您就可以使来自 gcc 的任何警告静音。

具有结构的代码API没有给出任何警告的原因是它void*可以static_cast指向任何指向对象的指针。我认为您在访问数据成员时违反了严格的别名,因为API当该位置的实际对象是void*. void*但是因为您的结构的布局与您的实现中的 a 和指向函数的指针的布局相同,所以它无论如何都可以工作。从理论上讲,即使使用相同的布局,它也可能因为严格的混叠违规而中断(您使用的优化越多)。

避免严格混叠违规的安全方法是:与Kerrekstd::memcpy(&fp, &p, sizeof p)的相同,但由于需要一个完整的类型,因此需要std::copy较少的reinterpret_casts混乱。除了避免严格的混叠违规外,这还方便地避免了任何诊断。您不再在函数和对象指针之间进行转换:您直接将一个对象表示复制到另一个。只要保证 的对象表示与指向函数的指针相同(在 Posix 中就是这种情况),这将起作用。memcpyvoid*std::copyvoid*

于 2012-09-25T14:53:20.167 回答
1

完全符合标准的解决方案:

extern "C" typedef int (func_t)(char, double); // desired API function signature

int main()
{
    static_assert(sizeof(void *) == sizeof(func_t *), "pointer cast impossible");

    void       *  p = dlsym(handle, "magic_function");
    char const * cp = reinterpret_cast<char const *>(&p);

    func_t     * fp;

    std::copy(cp, cp + sizeof p, reinterpret_cast<char *>(&fp));

    return fp('a', 1.25);
}

一种更简单但更可疑的写法是使用一些类型双关语:

static_assert(sizeof(void *) == sizeof(func_t *), "pointer cast impossible");

void   * vp = dlsym(handle, "magic_function");
func_t * fp;

*reinterpret_cast<void **>(&fp) = vp;  // this is type-punning
于 2012-09-25T14:28:30.130 回答
0

如果您将函数指针包装在结构中,那么您可以通过添加额外的间接级别来解决问题。想象一下,如果一个函数指针占用 10 个字节,而一个对象指针占用 4 个字节。您的结构至少有 10 个字节,并且您将有一个 4 字节的指针。这一切都很好。当您访问该结构以获取您的函数指针时,您提取了完整的 10 个字节。什么都没有丢失。但是,如果要将 10 字节的函数指针转换为 4 字节的对象指针,则必然会丢失 6 字节的信息。

这不是一个实际的问题,因为支持 dlsym 的平台必须有足够大的 void 指针来存储函数指针的地址,但是编译器试图阻止您编写不可移植的代码。

于 2012-09-25T14:46:45.277 回答