バックグラウンド:
私は、C++ GNU/Linux アプリケーションを Windows に移植するという、うらやましい仕事をしていることに気づきました。このアプリケーションが行うことの 1 つは、特定のパスで共有ライブラリを検索し、posix dlopen() および dlsym() 呼び出しを使用してそれらからクラスを動的にロードすることです。このようにロードするのには十分な理由がありますが、ここでは触れません。
問題:
dlsym() または GetProcAddress() を使用して C++ コンパイラによって生成されたシンボルを動的に検出するには、extern "C" リンケージ ブロックを使用してシンボルをアンマングルする必要があります。例えば:
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string> get_list()
{
list<string> myList;
myList.push_back("list object");
return myList;
}
}
このコードは完全に有効な C++ であり、Linux と Windows の両方の多数のコンパイラでコンパイルおよび実行されます。ただし、「戻り値の型が有効な C ではない」ため、MSVC ではコンパイルされません。私たちが思いついた回避策は、リスト オブジェクトの代わりにリストへのポインターを返すように関数を変更することです。
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string>* get_list()
{
list<string>* myList = new list<string>();
myList->push_back("ptr to list");
return myList;
}
}
私は、新しい関数と古いレガシー関数プロトタイプの両方で動作するか、少なくとも非推奨の関数が検出されたときに検出して警告を発する、GNU/Linux ローダーの最適なソリューションを見つけようとしています。古いライブラリを使用しようとしたときにコードがセグメンテーション違反を起こした場合、ユーザーにとって見苦しいものになります。私の最初のアイデアは、get_list の呼び出し中に SIGSEGV シグナル ハンドラーを設定することでした (これが厄介なのはわかっています。より良いアイデアを受け入れます)。したがって、古いライブラリをロードするとセグメンテーション違反が発生することを確認するために、古い関数プロトタイプ (リスト オブジェクトを返す) を使用して新しいロード コード (リストへのポインタを期待する) を使用してライブラリを実行し、驚いたことにそれを実行しました。ちょうど働いた。私が持っている質問は、なぜですか?
以下の読み込みコードは、上記の両方の関数プロトタイプで動作します。gcc バージョン 4.1.2 および 4.4.4 を使用して、Fedora 12、RedHat 5.5、および RedHawk 5.1 で動作することを確認しました。-shared および -fPIC を指定して g++ を使用してライブラリをコンパイルし、実行可能ファイルを dl (-ldl) に対してリンクする必要があります。
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <string>
using std::list;
using std::string;
int main(int argc, char **argv)
{
void *handle;
list<string>* (*getList)(void);
char *error;
handle = dlopen("library path", RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&getList) = dlsym(handle, "get_list");
if ((error = dlerror()) != NULL)
{
printf("%s\n", error);
exit(EXIT_FAILURE);
}
list<string>* libList = (*getList)();
for(list<string>::iterator iter = libList->begin();
iter != libList->end(); iter++)
{
printf("\t%s\n", iter->c_str());
}
dlclose(handle);
exit(EXIT_SUCCESS);
}