我创建了一个名为libmathClass.so的测试库,将从下面的代码中加载该库。该共享库具有一个类,并且创建了库调用以返回该类的对象。 如何从下面显示的主要代码中调用该对象的方法。我从ld(linker)收到未定义的引用错误,因为它不知道方法的定义。
void* handle;
handle=dlopen("math1/libmathClass.so", RTLD_LAZY);
if(!handle)
{
cout<<"error loading library: "<<dlerror()<<endl;
exit(2);
}
else
{
cout<<"***libmathClass.so library load successful!"<<endl;
}
void* (*mathInit) ();
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");
if(!mathInit)
{
cout<<"error loading instance method: "<<dlerror()<<endl;
exit(3);
}
else
{
cout<<"***method load successful!"<<endl;
}
mathOperationClass *mathInstance;
auto obj = (*mathInit)();
if(!obj)
{
cout<<"object is not created"<<endl;
exit(4);
}
else
{
cout<<"object created!!!"<<endl;
mathInstance = reinterpret_cast<mathOperationClass *>(obj);
}
int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
cout<< mathInstance->AddInt(num1, num2)<<endl;
我以前用来编译的命令-g ++ --std = c ++ 11 -g -o dynamicTest dynamicMain.cpp -ldl
错误消息: dynamicMain.cpp:54:对`mathOperationClass :: AddInt(int,int)'的未定义引用 collect2:错误:ld返回1退出状态
答案 0 :(得分:1)
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");
您在这里使用dlsym()
在共享库中找到此符号。这必须是具有C
链接的函数,因为符号名称没有被修饰。这很重要,当您盯着这条线时,请记住这一点:
cout<< mathInstance->AddInt(num1, num2)<<endl;
在这里,AddInt
是mathInstance
指向的类的方法。类方法只是另一个函数,除了它始终将隐藏的this
指针作为附加参数。用这么多的话来说,这就是类方法的事实,而事实证明,典型的C ++实现就是这种情况。从技术上讲,C ++实际上并不需要这种情况。 C ++实现可以自由地以任何方式产生符合C ++规范的结果的方法。但是,实际上,在典型的C ++实现中,这实际上是一个类方法。具有附加参数的函数,其引用为this
。
因此,从某种意义上说,以上一行基本上等同于:
cout<< mathOperationClass::AddInt(mathInstance, num1, num2)<<endl;
这基本上就是这里发生的事情,说得很松散。
此mathOperationClass::AddInt
方法/函数大概位于您dlopen
编辑的同一共享库中;并且因为您dlopen
编辑了它,但实际上并未链接到它,所以您有对该符号的引用,并且该引用无法在运行时解析,因此您的运行时未定义符号错误。
您甚至可以丝毫希望调用该类方法(如果可以完全以这种方式调用)的唯一方法是也使用dlsym()
。但是,要想真正做到这一点,甚至要有一点点祈祷,就需要一整件事。
首先,您必须找出actual mangled C++ symbol name。使用我的Linux x86_64 g ++编译器作为参考,此方法的错误名称将为“ _ZN18mathOperationClass6AddIntEii”。有了它,您就可以使用dlsym
在共享库中找到此符号(或此方法的C ++实现的实际错误符号名称)。
一旦有了这个符号,现在怎么办?好吧,我们希望您的C ++实现确实具有一个可破解的C ++ ABI,您可以在其中通过显式传递一个额外的this
参数来调用类方法,例如:
int (*addInt)(mathOperationClass *, int, int)=
reinterpret_cast<int (*)(mathOperationClass *, int, int)>
(dlsym(handle, "_ZN18mathOperationClass6AddIntEii"));
cout << (*addInt)(mathInstance, num1, num2) << endl;
除非可以确认C ++方法可以在您的C ++实现的ABI中以这种怪异的方式调用,否则整个纸牌屋将崩溃。由于您已经在使用dlopen
(),因此您已经在非便携式领域中,使用了C ++实现特定的资源,因此您可能还需要弄清楚是否可以用这种方式调用C ++方法。如果没有,则必须使用普通指针弄清楚如何调用它们。
已说完上述所有内容
有一种方法可以避免处理此类混乱情况:将此类方法设为虚拟类方法。虚拟类方法是通过内部虚拟函数表调度的。因此,只需尝试将此AddInt
方法声明为虚拟类方法并按原样调用即可。这很可能在您的C ++实现中起作用,因为在这种情况下,编译器将不会为mathOperationClass::AddInt
发出明确的符号引用。它将通过虚拟函数表找到该方法,该表已悄悄地附加到对象的每个实例。
当然,您还需要牢记什么是虚函数及其含义。但是,在几乎所有情况下,这都是一种调用从共享库动态加载的类的方法的廉价方法。