1

私の Linux ボックスには、2 つのライブラリがあります。

libfoo1.a and libfoo2.a

両方とも実装が含まれています

void foo(int)

私のメインプログラムはfooを呼び出します:

int main() { foo(1); return 0; }

g ++を使用してプログラムを2つの方法でコンパイルしました

g++ main.cpp libfoo1.a libfoo2.a -o a1.out
g++ main.cpp libfoo2.a libfoo1.a -o a2.out

プログラムを実行すると、a1 は明らかに libfoo1.a の foo() 実装を使用していますが、a2 は明らかに libfoo2 を使用しています。つまり、g++ は最初に見た foo() をリンクします。

私の質問 (最後に) は、この「貪欲な」リンク ポリシーは実際に C++ 標準で指定されているのでしょうか? または、異なるコンパイラ/プラットフォームは、実装定義の方法で異なる動作をしますか?

PS: 質問を実際的な文脈に置くために、私はこの g++ の例が機能する方法が本当に好きです。私の実際のアプリケーションでは、多くの (多くの!) 関数を実装するレガシー libfoo2 がありますが、libfoo1 でそれらの一握りの新しい実装を提供したいと考えています。一方では、libfoo1 にまったく新しいインターフェースを書き、ほんの一握りを実装してから、残りを libfoo2 に委譲することができます。しかし、リンカーに頼って委任できるのであれば (icc のような非 g++ コンパイラの場合でも)、委譲コードをすべて書くことは避けたいと思います。

PPS:実際の実用的なコンテキストで言えば、libfoo2 は blas であり、libfoo1 はそのルーチンのいくつかの自作 OpenMP 実装です。私は MKL のために砲撃する準備ができていません。ATLAS は、呼び出したい関数をマルチスレッド化しません。GEMM のマルチスレッド化には非常に優れていますが、LAPACK のより風変わりなルーチンも高速である必要があります (zsptrf / zsptrs / zspr)。これらのルーチンのキャッシュを無視した OpenMP 実装は、キャッシュ調整された順次実装よりも優れているようです。

投稿が長くなって申し訳ありません。

4

4 に答える 4

3

あなたが説明したように、ほとんどすべてのリンカが動作します。

リンカーの従来の動作は、コマンド ラインで指定されたライブラリ内で左から右に外部関数を検索することです。つまり、関数の定義を含むライブラリは、それを使用するソース ファイルまたはオブジェクト ファイルの後に表示する必要があります。

GCCの紹介から。

于 2009-12-18T15:12:46.160 に答える
3

標準では、リンクの順序については何も述べていません。コンパイラが使用する順序に依存することはお勧めできません。

于 2009-12-18T15:07:30.693 に答える
2

標準によると、同じ関数の 2 つの定義があり、これは 1 つの定義規則に違反しています。その結果、未定義の動作になります。

おそらく、これを処理する「正しい」方法は、元のアーカイブを取得して、そこからすべてのオブジェクト ファイルを抽出することです。次に、新しいオブジェクト ファイルをディレクトリにコピーし、必要なバージョンのオブジェクト ファイルのみを含む新しいライブラリを作成します。最後に、結合したライブラリとリンクします (元のライブラリの何も、置き換えた関数について文書化されていないものに依存していないことを願っています)。

于 2009-12-18T16:03:10.843 に答える
1

私が知る限り、C++ 標準にはリンカーは含まれていません。そのため、たとえば、mingw32 と microsoft の C++ コンパイラは、異なる名前マングリング スキームを使用しても問題を解決できます。

于 2009-12-18T15:14:46.450 に答える