3

この件に関して見つけたすべてのチュートリアルを何日も読んで読み直し、SO で関連する質問を何時間も (さらには何日も) 閲覧しましたが、まだ次のことを行うことができません。これが重複している場合は、申し訳ありません。重複した質問を何度も見て再読した可能性がありますが、私の問題に対する回答の関連性を理解できませんでした。それはさておき...

アプリケーションにプラグイン アーキテクチャを実装しようとしています。プラグインはコンパイルされ、ライブラリとしてインストールされます。実行時に、アプリケーションは dlopen() / dlsym() を使用して、プラグインの関数をロードおよびリンクします。
プラグイン (ライブラリ) が一連の関数を実装して、データをメインのアプリケーションに返したり、アプリケーションから渡されたデータを操作したりするという考え方です。

このアイデアをテストするために、プラグイン自体の (人間が読める) 名前を (std::string として) 返す関数を (プラグイン内に) 実装しようとしました。簡単に始められると思いました.... :-/

これが私がこれまでに得たものです:

// Plugin.cpp
extern "C" void plugin_name(std::string *name) {
        name = new std::string("Example plugin name");
}

// Application.cpp
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string*);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.

プラグイン名が返される、より単純に見える(しかしうまく機能しなかった)ものを含め、さまざまな組み合わせを試しました。

// Plugin.cpp
extern "C" std::string plugin_name(void) {
        return std::string("Example plugin name");
}

コードがコンパイルされ、アプリケーションがクラッシュしなくなりました ;)
しかし、実際のプラグイン名が表示されるはずの場所に空白があります。

これまでに読んだすべてのチュートリアルでは、データが双方向に渡されるメカニズムについて非常に簡単に説明しています: プラグイン <=> アプリケーション。「単純な」 std::string でやろうとしていることは、後でもっと複雑なオブジェクトでやりたいと思います (つまり、プラグイン関数は参照によってオブジェクトを取得し、そのプロパティの一部を変更します)。チュートリアルは多かれ少なかれすべて dlsym() を使用してポインターを作成する時点で停止し、このポインターの使用方法について多くの例を示していません。

それで、それをすべて行う方法は?

別の関連する質問: アプリケーションとプラグインの両方で使用する共通ヘッダーを使用する必要がありますか? 関数呼び出しの署名を定義する場所はどこですか? どうすればこれを行うことができ、それはどのように役立ちますか?

4

3 に答える 3

5

関数のシグネチャは、その名前と引数の型から生成されます(戻り値の型は関係ありません)。extern "C"を使用して関数を宣言すると、Cシンボルの命名スキームが使用されます。これは明らかにstd::stringのようなC++型を処理できません。そのため、引数としてstd::stringを渡すことはできません。

std::stringを返すことが機能しない理由を説明できません。おそらく、異なる呼び出し規約が使用されます。

とにかく、共有ライブラリからC ++コードをインポートする正しい方法は、エントリポイントからC++タイプへのポインタを返すことです。また、このエントリポイントには、Cで使用可能な型の引数が必要です(エントリポイントは、共有ライブラリからエクスポートされたドキュメント化された関数です)。

これは、共有ライブラリからC++クラスをロードする基本的な側面に関する優れた記事です。この記事はあなたの質問に完全に答えます。

共有ライブラリからメインアプリケーションにスローされる例外を使用する場合、落とし穴があることに注意してください。そして、ライブラリ内に作成されたオブジェクトのdynamic_castを使用します。この問題に直面したときにある程度準備できるように、このトピックについて説明しました。

[編集]

私の答えをより明確にするために、いくつかの例を追加します。

プラグイン名を取得するには、次を使用できます。

extern "C" const char * plugin_name() {
    return "Example plugin name";
}

// main.cc:
void* handle = dlopen("libplugin.so", RTLD_LAZY);
// ...
typedef const char * (*plugin_t)();
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ...
std::string my_plugin_name(call_plugin_name());
// use it

プラグイン機能を実際に使用するには、ヘッダーで基本クラスを宣言する必要があります。

// plugin.h
class Plugin {
    public:
        virtual void doStuff() = 0;
        virtual ~Plugin() = 0;
};

// plugin.cc
Plugin::~Plugin() {
}

// myplugin.cc
class MyPlugin : public Plugin {
    virtual void doStuff() {
        std::cout << "Hello from plugin" << std::endl;
    }
};

extern "C" Plugin *createMyPluginInstance() {
    return new MyPlugin;
}
于 2011-01-17T13:29:59.783 に答える
2

試す:

 extern "C" void plugin_name(std::string **name) {
     *name = new std::string("Example plugin name");
 }

 ...

 std::string *my_plugin_name;
 call_plugin_name(&my_plugin_name);

引数として渡したポインタのコピーを割り当てるため、割り当てる予定のポインタではありません。

編集ここに行きます:ファイルmain.cpp

#include <iostream>
#include <dlfcn.h>
#include <string>

// Application.cpp
int main() {
    void* handle = dlopen("libplugin.so", RTLD_LAZY);
    typedef void (*plugin_t)(std::string**);
    dlerror(); // Reset errors.
    plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
    // ... Some error handling code.
    std::string *my_plugin_name;
    call_plugin_name(&my_plugin_name);
    dlclose(handle);
    // More code that displays my_plugin_name.
    std::cout << "Plugin name is " << *my_plugin_name << std::endl;
    delete my_plugin_name;
    return 0;
}

ファイルplugin.cpp

#include <string>

extern "C" void plugin_name(std::string **name) {
    *name = new std::string("example plugin name");
}

警告の一言。これはコンパイルして実行しますが、dll境界を越えてC ++タイプを渡すのは危険であり、上記のコードはコンパイルして実行するのに十分に修正されたコードであり、安全ではなく、非常に明示的なメモリ処理があります。別の方法で問題を攻撃することもできます。

于 2011-01-17T13:02:57.177 に答える
1

この質問とその回答を読んでください。C++ では、共有ライブラリの境界を越えて非互換性が生じる可能性が数多くあります。

于 2011-01-17T13:30:21.267 に答える