Mar*_*rio 7 c++ struct compilation function-pointers shared-libraries
昨天我正在研究动态加载共享对象以及获取函数指针.
我多次被告知,ISO C++标准禁止通过void指针共享指向函数的指针,并且仍然需要解决问题.
在阅读了Johan Petterson的文章"关于dlsym的问题"后,我更了解原因,并且我也明白被标准禁止并不意味着你绝对不能使用它.否则,所有C++程序员如何使用具有正确ISO C++代码的共享对象的函数?只是猜测,我可能是错的,我不是很熟悉C++.
在试验我的代码时,我发现通过共享指向结构的指针,该结构包含对我想要调用的函数的引用,我的编译器不会抱怨.我在编译时使用-Wall和-pedantic.
我的代码如下:
myclass.hpp
class myclass
{
public:
virtual void dosomething (void)=0;
}
Run Code Online (Sandbox Code Playgroud)
api.hpp
#include <myclass.hpp>
struct API
{
myclass* (* func)(void);
};
Run Code Online (Sandbox Code Playgroud)
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
};
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
已经编译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]
Run Code Online (Sandbox Code Playgroud)
我知道这只是一个警告,但是为什么不是第一种情况下的警告打印,当使用结构时,如果最终我间接能够引用指向驻留在共享对象中的函数的指针?
说到这一点,任何人都知道以完全正确的ISO C++方式实现所有这些的方法吗?它甚至存在吗?
如果您将函数指针封装在结构中,那么您可以通过添加额外的间接级别来解决该问题。想象一下,如果一个函数指针占用 10 个字节,但一个对象指针占用 4 个字节。您的结构体至少有 10 个字节,并且您将有一个指向该结构体的 4 字节指针。这一切都很好。当您访问该结构体以获取函数指针时,您将提取完整的 10 个字节。什么都没有丢失。但是,如果要将 10 字节函数指针转换为 4 字节对象指针,则必然会丢失 6 字节信息。
这在这里不是一个实际问题,因为支持 dlsym 的平台必须具有足够大的 void 指针来存储函数指针的地址,但编译器试图阻止您编写不可移植的代码。