Вызов функции класса non stati c из динамически загружаемой библиотеки - PullRequest
1 голос
/ 09 апреля 2020

Я пишу приложение с дополнительной зависимостью времени выполнения с KWallet. Это означает, что если в пользовательской системе установлен KWallet, а если нет, то он все еще работает, но без поддержки KWallet.

Вот как я загружаю библиотеку, это свойство stati c моего класса-оболочки. Затем в конструкторе при условии состояния я разрешаю символы из библиотеки.

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 - функция также является членом c членов моего класса, но я не уверен, что это хорошая идея. Вот определения из моего класса

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);

Все идет хорошо, пока мне не понадобится выполнить элементы, не являющиеся c и, особенно , деструктор. Насколько я знаю, он должен выглядеть так

((*wallet).(destructor))()

, но, похоже, это не сработает. Я совершенно новичок в этой топи c и понятия не имею, даже если я начал правильно.

Итак, как мне вызвать деструктор этого загруженного класса? Как я могу призвать остальных его членов? Или мне лучше сделать это каким-то совершенно другим способом?

PS Я знаю, что есть API-интерфейс DBUS для KWallet, даже некоторые библиотеки-обертки, такие как qtkeychain, но я хочу понять, как это сделать. зависимостей, использующих этот пример.

Ответы [ 2 ]

1 голос
/ 10 апреля 2020

Я нашел решение.

Идея состоит в том, чтобы написать небольшую разделяемую библиотеку с такими функциями оболочки

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 и целевую ссылку во время сборки на реальную зависимость, 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);
0 голосов
/ 09 апреля 2020

Прежде чем перейти к решению, я должен заявить, что это очень плохая идея, и я не вижу разумной причины для go таким образом, кроме случаев, когда вы используете класс из скомпилированной разделяемой библиотеки, которую вы не можете редактировать. Исходный код и класс не имеют альтернативного виртуального интерфейса.

Лучший способ сделать это в c ++ - это использовать виртуальные методы, создав базовый класс, содержащий необходимые функции, и любой подкласс в общей библиотеке может переопределить эти виртуальные методы для настройки поведения.

Теперь это решение для вашего случая:

Не статичный c метод класса имеет соглашение о вызовах thiscall, что означает, что они похожи на обычные функции, за исключением того, что они принимают указатель на экземпляр класса в качестве первого аргумента, это указатель this! Фактически методы в c ++ (не виртуальные) - это syntacti c sugar для c функций, которые работают с struct

Этот фрагмент кода иллюстрирует:

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;
};

Так что в вашем случае : для каждого объявления метода добавьте указатель на экземпляр класса, который является первым аргументом, а когда вы хотите вызвать этот метод для экземпляра, просто передайте его в качестве первого указателя.

Виртуальные функции реализуются двумя способами:

1 - vtable внутри самого класса, например c vtables

2 - указатель на vtable внутри класса, поэтому у вас есть только один vtable на объявление класса, и сказано, что этот метод лучше для кеша, поэтому он используется большинством компиляторов

...