昨天我正在研究共享对象的动态加载和获取函数指针。
我多次被告知,通过 void 指针共享指向函数的指针是 ISO C++ 标准禁止的,并且仍然是一个有待解决的问题。
看完Johan Petterson’s artitle “about the problem with dlsym”我更明白其中的原因,我也明白被标准禁止并不意味着你绝对不能使用它。否则,所有 C++ 程序员如何使用正确的 ISO C++ 代码使用来自共享对象的函数?只是猜测,我可能是错的,我不是 C++ 专家。
在试验我的代码时,我发现通过共享指向包含对我要调用的函数的引用的结构的指针,我的编译器不会报错。我在编译时使用 -Wall 和 -pedantic。
我的代码如下所示:
myclass.hpp
class myclass
{
public:
virtual void dosomething (void)=0;
}
api.hpp
#include <myclass.hpp>
struct API
{
myclass* (* func)(void);
};
so.hpp
#include <iostream>
#include "myclass.cpp"
#include "api.hpp"
class childclass : public myclass
{
void dosomething (void)
{
std::cout << "Did it.\n";
}
}
/* function to return a new instance of childclass */
extern "C" myclass* make (void)
{
return new childclass;
}
/* struct that contains a pointer to the function */
extern "C" API interface;
API interface
{
make
};
host.cpp
#include <iostream>
#include <dlfcn.h>
#include "myclass.hpp"
#include "api.hpp"
int main (void)
{
void * th = dlopen("./so.so", RTLD_LAZY);
/* error checking was here */
#ifndef usefunction
API* api = static_cast<API*>( dlsym(th, "interface") );
myclass * inst = api->make();
inst->dosomething();
#else
myclass* (*func)(void) = reinterpret_cast<myclass* (*)(void)>( dlsym(th, "make") );
/* will never get to this point */
#endif
return 0;
}
已经编译了 so.so
,然后我编译了我的 host.cpp
文件。
g++ -ldl -Wall -pedantic host.cpp -o host
编译正常,程序正确打印 Did it.
运行时。
g++ -ldl -Wall -pedantic host.cpp -o host -Dusefunction
提示
In function ‘int main(int, char**)’:
warning: ISO C++ forbids casting between pointer-to-function
and pointer-to-object [enabled by default]
我知道这只是一个警告,但为什么在第一种情况下不打印警告,当使用结构时,如果最终我能够间接引用指向驻留在共享对象中的函数的指针?
说到这里,有人知道以完全正确的 ISO C++ 方式实现所有这些的方法吗?它甚至存在吗?
最佳答案
完全符合标准的解决方案:
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
关于c++ - 当我将 void 指针导入到具有指向共享对象中函数的指针的结构时,我没有收到警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12585030/