我目前正在开发一个插件库,其中一个人不仅可以导入C-Linkage符号,还可以导入所有导入的东西。
到目前为止,虽然问题在于,gcc screw成员函数调用了。
如果我导出以下内容:
static member_function(Class* c)
{ c->method();}
它可以正常工作,我可以访问类成员。但如果我做以下事情:
void (Class ::*p)() = import("Class::method");
(x.*p)();
我得到了正确的指针,也能够调用函数和传递的参数,但是这个指针指向了必杀技。我认为gcc从堆栈的错误位置或类似的东西中取出它。
它适用于MSVC。
我正在使用mingw-w64 5.1。
有谁知道错误是什么?
#include <iostream>
namespace space {
class __declspec(dllexport) SomeExportThingy
{
int i = 42;
public:
virtual void __declspec(dllexport) Method(int*) const
{
using namespace std;
cout << "Calling Method" << endl;
cout << pi << endl;
cout << *pi << endl;
cout << this << endl;
cout << this->i << endl;
}
}
}
namespace space {
class SomeExportThingy
{
///dummy to have some data in the address
int dummy[20];
};
int main()
{
auto h = LoadLibrary("plugin.dll");
auto p = GetProcAddress(h, "_ZNK5space16SomeExportThingy6MethodEPi");
typedef void (space::SomeExportThingy::*mptr)(int*) const;
///used because posix passed void*
auto fp = *reinterpret_cast<mptr*>(&p);
space::SomeExportThingy st;
int value = 22;
cout << "ValueLoc: " << &value << endl;
cout << "StLoc: " << &st << endl;
(st.*fp)(&value);
}
现在发生的是,调用该函数并正确传递指向pi的指针。但是,这个指针完全搞砸了。 再说一遍:它适用于MSVC,它正确地得到了这个指针,但是gcc得到了这个错误。 我不知道为什么会发生这种情况,从方法中删除虚拟也不会改变它。 我不知道是什么导致这种情况,所以也许有人知道ABI在这里做了什么。
以下是我得到的指示:
我无法找到值之间的任何关系
答案 0 :(得分:3)
这不适用于GCC:
typedef void (space::SomeExportThingy::*mptr)(int*) const;
///used because posix passed void*
auto fp = *reinterpret_cast<mptr*>(&p);
指向成员的指针的表示是普通函数指针(或void*
)的两倍,因此您从只包含一个单词的内存位置读取两个单词。第二个单词(告诉编译器如何调整调用的this
指针)是垃圾,它只是在堆栈上p
之后发生的任何事情。
请参阅https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html:
在C ++中,指向成员函数(PMF)的指针是使用宽泛的指针来实现的,以处理所有可能的调用机制; PMF需要存储有关如何调整'this'指针的信息,
p
是void*
所以它是堆栈上占用sizeof(void*)
个字节的内存位置。 &p
是指向该内存位置的指针。 reinterpret_cast<mptr*>(&p)
是指向同一地址的2*sizeof(void*)
个字节的指针。 *reinterpret_cast<mptr*>(&p)
从内存位置读取2*sizeof(void*)
个字节,其大小仅为sizeof(void*)
个字节。 答案 1 :(得分:1)
对于linux,动态函数加载的函数是:dlopen(),dlsym()和dlclose()。请参考:dlopen() man page。
考虑到C ++方法名称已经被破坏了。并且他们有一个看不见的&#39; *这个&#39;参数在所有其他参数之前传递。这两个问题共同使得在使用动态链接时尝试直接访问C ++对象并非易事。
我发现最简单的解决方案是使用&#39; C&#39;公开对C ++对象实例的访问的函数。
其次,当实例化的代码在.so库对象中时,C ++对象的内存管理并不简单,尽管引用代码来自用户的应用程序。
关于为什么避免使用指向C ++成员方法的指针很困难,请参考:ISO CPP Reference, Pointers to Methods。
/** File: MyClass.h **/
// Explicitly ensure 'MyClassLoaderFunc' is NOT name mangled.
extern 'C' MyClass* MyClassLoaderFunc(p1, p2 ,p3, etc );
extern 'C' MyClass* MyClassDestroyerFunc(MyClass* p);
// Create function pointer typedef named 'LoaderFuncPtr'
typedef MyClass*(MyClassLoaderFunc* LoaderFuncPtr)(p1,p2,p3,etc);
// Define MyClass
class MyClass
{
/** methods & members for the class go here **/
char dummy[25];
int method( const char *data);
};
/** File: MyClass.cpp **/
#include "MyClass.h"
MyClass* MyLoaderFunc(p1, p2 ,p3, etc) {
MyClass* newInstance = new MyClass::CreateInstance( p1, p2, p3, etc);
/** Do something with newInstance **/
return newInstance;
}
MyClass::method(const char* data)
{
}
/** File: MyProgram.cpp **/
#include "MyClass.h"
main()
{
// Dynamically load in the library containing the object's code.
void *myClassLibrary = dlopen("path/to/MyClass.so",RTLD_LOCAL);
// Dynamically resolve the unmangled 'C' function name that
// provides the bootstrap access to the MyClass*
LoaderFuncPtr loaderPtr = dlsym(myClassLibrary,"MyClassLoaderFunc");
DestroyFuncPtr destroyerPtr = dlsym(myClassLibrary,"MyClassDestroyerFunc");
// Use dynamic function to retrieve an instance of MyClass.
MyClass* myClassPtr = loadPtr(p1,p2,p3,etc);
// Do something with MyClass
myClassPtr->method();
// Cleanup of object should happen within original .cpp file
destroyPtr(myClassPtr);
myClassPtr = NULL;
// Release resources
dlclose(myClassLibrary);
return 0;
}
希望这会有所帮助..
我还建议将工厂范例作为一种更强大的解决方案,我将留给读者进行探索。
答案 2 :(得分:0)
正如Jonathan所指出的,指向成员的指针比正常的函数指针更大 最简单的解决方案是保留并初始化额外的空间。
typedef void (space::SomeExportThingy::*mptr)(int*) const;
union {
mptr fp;
struct {
FARPROC function;
size_t offset;
};
} combFp;
combFp.function = p;
combFp.offset = 0;
auto fp = combFp.fp;