実行時にコンパイル済みの C コードをロードし、その中で関数を呼び出すにはどうすればよいでしょうか? 単純に exec() を呼び出すようなものではありません。
EDIT:モジュールをロードするプログラムはCです。
実行時にコンパイル済みの C コードをロードし、その中で関数を呼び出すにはどうすればよいでしょうか? 単純に exec() を呼び出すようなものではありません。
EDIT:モジュールをロードするプログラムはCです。
dlopenは行く方法です。次にいくつかの例を示します。
dlopenを使用したプラグインのロード:
#include <dlfcn.h>
...
int
main (const int argc, const char *argv[])
{
char *plugin_name;
char file_name[80];
void *plugin;
...
plugin = dlopen(file_name, RTLD_NOW);
if (!plugin)
{
fatal("Cannot load %s: %s", plugin_name, dlerror ());
}
上記のコンパイル:
% cc -ldl -o program program.o
次に、プラグイン用にこのAPIを想定します。
/* The functions we will find in the plugin */
typedef void (*init_f) ();
init_f init;
typedef int (*query_f) ();
query_f query;
プラグインでinit()のアドレスを見つける:
init = dlsym(plugin, "init");
result = dlerror();
if (result)
{
fatal("Cannot find init in %s: %s", plugin_name, result);
}
init();
他の関数であるquery()は、次の値を返します。
query = dlsym (plugin, "query");
result = dlerror();
if (result)
{
fatal("Cannot find query in %s: %s", plugin_name, result);
}
printf("Result of plugin %s is %d\n", plugin_name, query ());
行で完全な例を取得できます。
Linux / UNIX では、POSIX // dlopen
/dlsym
関数を使用して共有ライブラリを動的に開き、それらが提供するシンボル (関数を含む) にアクセスできます。詳細については、man ページを参照してください。dlerror
dlclose
この質問には回答がありますが、このトピックに関心のある他の人は、古いプラグイン ベースのアプリケーションからのクロス プラットフォームの例を高く評価するかもしれないと考えています。この例は win32 または linux で動作し、ファイル引数で指定された動的にロードされる .so または .dll で「コンストラクター」と呼ばれる関数を検索して呼び出します。例は c++ ですが、手順は c でも同じです。
//firstly the includes
#if !defined WIN32
#include <dlfcn.h>
#include <sys/types.h>
#else
#include <windows.h>
#endif
//define the plugin's constructor function type named PConst
typedef tcnplugin* (*PConst)(tcnplugin*,tcnplugin*,HANDLE);
//loads a single specified tcnplugin,allmychildren[0] = null plugin
int tcnplugin::loadplugin(char *file) {
tcnplugin *hpi;
#if defined WIN32 //Load library windows style
HINSTANCE hplugin=LoadLibrary(file);
if (hplugin != NULL) {
PConst pinconstruct = (PConst)GetProcAddress(hplugin,"construct");
#else //Load it nix style
void * hplugin=dlopen(file,RTLD_NOW);
if (hplugin != NULL) {
PConst pinconstruct = (PConst)dlsym(hplugin,"construct");
#endif
if (pinconstruct != NULL) { //Try to call constructor function in dynamically loaded file, which returns a pointer to an instance of the plugin's class
hpi = pinconstruct(this, this, hstdout);
} else {
piprintf("Cannot find constructor export in plugin!\n");
return 0;
}
} else {
piprintf("Cannot open plugin!\n");
#if !defined WIN32
perror(dlerror());
#endif
return 0;
}
return addchild(hpi); //add pointer to plugin's class to our list of plugins
}
また、呼び出したい関数のモジュールが c++ で記述されている場合は、次のように extern "C" を使用して関数を宣言する必要があることにも注意してください。
extern "C" pcparport * construct(tcnplugin *tcnptr,tcnplugin *parent) {
return new pcparport(tcnptr,parent,"PCPARPORT",0,1);
}
また、 cpluffを見ることもできます。これは、pure c 上のプラグイン管理ライブラリです。
フレームワークを検討する場合、Qt は QPluginLoader を提供します: Qt 5 ドキュメント(または古い Qt 4.8 ドキュメントについては、こちらを参照してください)
よりきめ細かな制御が必要な場合、Qt は QLibrary を使用してオンザフライでライブラリをロードする手段も提供します: Qt 5 ドキュメント(または古い Qt 4.8 ドキュメントについては、こちらを参照してください) 。
さらに良いことに、これらはプラットフォーム間で移植可能です。
Perl のような動的言語は常にこれを行います。Perl インタープリターは C で記述されており、多くの Perl モジュールは部分的に C で記述されています。これらのモジュールが必要な場合、コンパイルされた C コンポーネントはオンザフライで動的にロードされます。別の回答で述べたように、これらのモジュールを格納するメカニズムは、Windows では DLL であり、UNIX では共有ライブラリ (.so ファイル) です。UNIX で共有ライブラリをロードするための呼び出しは dlopen() だと思います。UNIX でこれを行う方法については、その呼び出しのドキュメントから始めることで、おそらくポインターを見つけることができます。Windows の場合、DLL を調査し、実行時に動的にロードする方法を学習する必要があります。[または、Cygwin UNIX エミュレーション レイヤーを通過する可能性があります。これにより、Windows で UNIX と同じ呼び出しを使用できるようになりますが、次の場合を除き、お勧めしません。
これは、共有ライブラリに対してリンクするだけとは異なることに注意してください。呼び出すコードを事前に正確に知っている場合は、共有ライブラリに対してビルドでき、ビルドはそのライブラリに「動的にリンク」されます。特別な処理をしなくても、ライブラリのルーチンは、プログラムが実際に呼び出したときにのみメモリにロードされます。しかし、ビルド時に任意のオブジェクト コード (現在は特定できないコード)をロードできるものを作成しようとしているのに、実行時に何らかの方法で選択されるのを待っている場合、それはできません。そのためには、dlopen() とその Windows のいとこを使用する必要があります。
Perl やその他の動的言語がこれを行う方法を調べて、実際の例を確認してください。この種の動的ロードを担当する Perl ライブラリは DynaLoader です。Perl と C コンポーネントの両方が含まれていると思います。Python のような他の動的言語にも似たようなものがあると確信しています。また、未リリースの Perl 6 用の仮想マシンである Parrot にも、これを行うためのメカニズムが確実に備わっています (または、将来的にそうなる予定です)。
さらに言えば、Java は JNI (Java Native Interface) インターフェースを介してこれを実現しているため、おそらく OpenJDK のソース コードを見て、Java が UNIX と Windows の両方でこれをどのように実現しているかを確認できます。
Windowsでは、これが私のやり方です。
生成/コンパイル/リンクの手順は、通常1秒未満で完了します。
DIYのアプローチがあります。これを行う方法 (および可能性) はシステムごとに異なりますが、一般的な考え方は、ファイルを開き、ファイルの内容をメモリに読み込み、そのメモリを実行可能にし、関数ポインタをこのメモリ内の有効な位置に初期化することです。 、そしてそこにいます。
もちろん、これは単なる実行可能コードであることを前提としています - ありそうもないことです。このコードでは、おそらくデータを RAM にロードする必要があり、グローバル/静的変数用のスペースが必要になる場合があります。これをすべて自分でロードすることもできますが、実行可能コードにアクセスして、その中のすべてのメモリ参照を調整する必要があります。
ほとんどのオペレーティング システムでは動的リンクが許可されており、これらすべてが自動的に行われます。