调用动态加载库的非静态class成员函数
Invoking non static class member function of dynamically loaded library
我正在用 KWallet 编写一个具有可选运行时依赖性的应用程序。这意味着如果用户系统上安装了 KWallet,它就会被使用,如果没有,它仍然可以工作,但没有 KWallet 支持。
这是我加载库的方式,它是我的包装器 class 的静态 属性。然后在状态条件下的构造函数中,我从库中解析符号。
QLibrary Core::PSE::KWallet::lib("KF5Wallet");
...
lib.load();
openWallet = (OpenWallet) lib.resolve("_ZN7KWallet6Wallet10openWalletERK7QStringyNS0_8OpenTypeE");
networkWallet = (NetworkWallet) lib.resolve("_ZN7KWallet6Wallet13NetworkWalletEv");
destructor = (Destructor) lib.resolve("_ZN7KWallet6WalletD2Ev");
与 QLibrary 一样 - 函数也是我的 class 的静态成员,但我不确定这是否是个好主意。
以下是我的 class
的定义
typedef ::KWallet::Wallet* (*OpenWallet)(const QString &, WId, ::KWallet::Wallet::OpenType);
typedef QString (*NetworkWallet)();
typedef void (*WalletOpened)(bool);
typedef void (*Destructor)();
static OpenWallet openWallet;
static NetworkWallet networkWallet;
static Destructor destructor;
这是我分配对象的方式
wallet = openWallet(networkWallet(), 0, ::KWallet::Wallet::Asynchronous);
一切顺利,直到我需要执行非静态成员,尤其是析构函数。据我所知,它应该是这样的
((*wallet).(destructor))()
但这似乎不起作用。我对这个话题完全陌生,即使我以正确的方式开始,我也不知道。
那么,如何调用这种方式加载的析构函数class?我如何调用它的其余成员?还是我最好用完全不同的方式来做?
P.S。我知道,KWallet 有一个 DBUS API,甚至是一些包装库,如 qtkeychain,但我想了解如何使用此示例建立这种依赖关系。
在进入解决方案之前,我应该声明这是一个非常糟糕的主意,我看不出这样做的合理理由,除非您使用编译共享库中的 class,您可以'编辑它的源代码,class 没有替代的虚拟接口。
在 C++ 中实现此目的的更好方法是通过创建包含您需要使用的功能的基础 class 来使用虚拟方法,并且共享库中的任何子 class 都可以覆盖那些虚拟方法自定义行为的方法。
现在这是您的案例的解决方案:
class 的非静态方法具有 thiscall
的调用约定,这意味着它们类似于普通函数,只是它们将指向 class 实例的指针作为第一个参数,这是 this
指针!实际上,c++(非虚拟)中的方法是对结构
进行操作的 c 函数的语法糖
这段代码说明了:
struct somestruct
{
int j;
};
void add(somestruct* this, int i)
{
this->j += i;
}
class someclass
{
public:
void add(int i)
{
j += i;
}
private:
int j;
};
所以在你的情况下:为每个方法声明添加一个指向 class 实例的指针,这是第一个参数,当你想在实例上调用这个方法时,只需将它作为第一个指针传递。
虚函数有两种实现方式:
1 - class 内部的 vtable 本身就像 c vtables
2 - 一个指向 class 内的 vtable 的指针,因此每个 class 声明只有一个 vtable,据说这种方法更适合缓存,因此大多数编译器都使用它
我找到了解决方案。
想法是编写一个带有类似包装函数的小型共享库
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
return KWallet::Wallet::openWallet(name, w, ot);
}
extern "C" void deleteWallet(KWallet::Wallet* w) {
w->deleteLater();
}
extern "C" const char* networkWallet() {
return KWallet::Wallet::NetworkWallet().toStdString().c_str();
}
extern "C" int readPassword(KWallet::Wallet* w, const QString &key, QString &value) {
return w->readPassword(key, value);
}
我们称这个小包装器为 foo.so
。所以,然后你构建这个 foo.so
并在构建时将 link 定位到真正的依赖项,在我的例子中是 KWallet
。
然后在主代码中,您将尝试动态加载此 foo.so
,而不是 KWallet
本身。如果 KWallet
在启动机器上不存在,这个 foo.so
根本不会加载,这就是我必须知道的技巧!
那么你当然可以像这样解析符号
QLibrary Core::PSE::KWallet::lib("foo");
...
lib.load();
openWallet = (OpenWallet) lib.resolve("openWallet");
networkWallet = (NetworkWallet) lib.resolve("networkWallet");
deleteWallet = (DeleteWallet) lib.resolve("deleteWallet");
readPassword = (ReadPassword) lib.resolve("readPassword");
然后这样称呼它
wallet = openWallet(networkWallet(), 0, ::KWallet::Wallet::Asynchronous);
...
QString password;
int result = readPassword(wallet, *i, password);
...
deleteWallet(wallet);
我正在用 KWallet 编写一个具有可选运行时依赖性的应用程序。这意味着如果用户系统上安装了 KWallet,它就会被使用,如果没有,它仍然可以工作,但没有 KWallet 支持。
这是我加载库的方式,它是我的包装器 class 的静态 属性。然后在状态条件下的构造函数中,我从库中解析符号。
QLibrary Core::PSE::KWallet::lib("KF5Wallet");
...
lib.load();
openWallet = (OpenWallet) lib.resolve("_ZN7KWallet6Wallet10openWalletERK7QStringyNS0_8OpenTypeE");
networkWallet = (NetworkWallet) lib.resolve("_ZN7KWallet6Wallet13NetworkWalletEv");
destructor = (Destructor) lib.resolve("_ZN7KWallet6WalletD2Ev");
与 QLibrary 一样 - 函数也是我的 class 的静态成员,但我不确定这是否是个好主意。 以下是我的 class
的定义typedef ::KWallet::Wallet* (*OpenWallet)(const QString &, WId, ::KWallet::Wallet::OpenType);
typedef QString (*NetworkWallet)();
typedef void (*WalletOpened)(bool);
typedef void (*Destructor)();
static OpenWallet openWallet;
static NetworkWallet networkWallet;
static Destructor destructor;
这是我分配对象的方式
wallet = openWallet(networkWallet(), 0, ::KWallet::Wallet::Asynchronous);
一切顺利,直到我需要执行非静态成员,尤其是析构函数。据我所知,它应该是这样的
((*wallet).(destructor))()
但这似乎不起作用。我对这个话题完全陌生,即使我以正确的方式开始,我也不知道。
那么,如何调用这种方式加载的析构函数class?我如何调用它的其余成员?还是我最好用完全不同的方式来做?
P.S。我知道,KWallet 有一个 DBUS API,甚至是一些包装库,如 qtkeychain,但我想了解如何使用此示例建立这种依赖关系。
在进入解决方案之前,我应该声明这是一个非常糟糕的主意,我看不出这样做的合理理由,除非您使用编译共享库中的 class,您可以'编辑它的源代码,class 没有替代的虚拟接口。
在 C++ 中实现此目的的更好方法是通过创建包含您需要使用的功能的基础 class 来使用虚拟方法,并且共享库中的任何子 class 都可以覆盖那些虚拟方法自定义行为的方法。
现在这是您的案例的解决方案:
class 的非静态方法具有 thiscall
的调用约定,这意味着它们类似于普通函数,只是它们将指向 class 实例的指针作为第一个参数,这是 this
指针!实际上,c++(非虚拟)中的方法是对结构
这段代码说明了:
struct somestruct
{
int j;
};
void add(somestruct* this, int i)
{
this->j += i;
}
class someclass
{
public:
void add(int i)
{
j += i;
}
private:
int j;
};
所以在你的情况下:为每个方法声明添加一个指向 class 实例的指针,这是第一个参数,当你想在实例上调用这个方法时,只需将它作为第一个指针传递。
虚函数有两种实现方式:
1 - class 内部的 vtable 本身就像 c vtables
2 - 一个指向 class 内的 vtable 的指针,因此每个 class 声明只有一个 vtable,据说这种方法更适合缓存,因此大多数编译器都使用它
我找到了解决方案。
想法是编写一个带有类似包装函数的小型共享库
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
return KWallet::Wallet::openWallet(name, w, ot);
}
extern "C" void deleteWallet(KWallet::Wallet* w) {
w->deleteLater();
}
extern "C" const char* networkWallet() {
return KWallet::Wallet::NetworkWallet().toStdString().c_str();
}
extern "C" int readPassword(KWallet::Wallet* w, const QString &key, QString &value) {
return w->readPassword(key, value);
}
我们称这个小包装器为 foo.so
。所以,然后你构建这个 foo.so
并在构建时将 link 定位到真正的依赖项,在我的例子中是 KWallet
。
然后在主代码中,您将尝试动态加载此 foo.so
,而不是 KWallet
本身。如果 KWallet
在启动机器上不存在,这个 foo.so
根本不会加载,这就是我必须知道的技巧!
那么你当然可以像这样解析符号
QLibrary Core::PSE::KWallet::lib("foo");
...
lib.load();
openWallet = (OpenWallet) lib.resolve("openWallet");
networkWallet = (NetworkWallet) lib.resolve("networkWallet");
deleteWallet = (DeleteWallet) lib.resolve("deleteWallet");
readPassword = (ReadPassword) lib.resolve("readPassword");
然后这样称呼它
wallet = openWallet(networkWallet(), 0, ::KWallet::Wallet::Asynchronous);
...
QString password;
int result = readPassword(wallet, *i, password);
...
deleteWallet(wallet);