2

ライブラリを書いていて、自分用に書いたユーティリティ関数がたくさんあるとしましょう。もちろん、これらの関数に外部リンケージを持たせて、ライブラリ ユーザーが混同しないようにする必要はありません (ほとんどの場合、関数の存在を外の世界に伝えないためです)。

一方、これらの関数は異なる翻訳単位で使用される可能性があるため、内部で共有する必要があります。

例を挙げましょう。いくつかのことを行うライブラリがあり、さまざまなソースファイルで必要になる場合があるためcopy_filecreate_directoryそれらをユーティリティ関数として実装します。

同じ名前の関数があるためにライブラリのユーザーが誤ってリンケージ エラーにならないようにするために、次の解決策を考えることができます。

  • ひどい方法:関数を使用するすべてのファイルに関数をコピーして貼り付け、static宣言に追加します。
  • 良い方法ではありません: それらをマクロとして記述します。私はマクロが好きですが、これはここではありません。
  • ユーザーが同じ名前を作成する可能性が十分に小さくなるように、奇妙な名前を付けます。これは機能するかもしれませんが、それらを使用するコードは非常に見苦しくなります。
  • 私が現在行っていること:static内部utils.hファイルに関数として記述し、そのファイルをソース ファイルに含めます。

最後のオプションは、1 つの問題があることを除けば、ほぼ問題なく動作します。関数の 1 つを使用しない場合、少なくとも警告が表示されます (関数は static として宣言されていますが、使用されていません)。私を狂ったと呼んでください、しかし私は自分のコードの警告を自由に保ちます。

私がやったのは、次のようなものでした。

ユーティリティ.h:

...
#ifdef USE_COPY_FILE
static int copy_file(/* args */)
{...}
#endif

#ifdef USE_CREATE_DIR
static int create_dir(/* args */)
{...}
#endif
...

file1.c:

#define USE_COPY_FILE
#define USE_CREATE_DIR
#include "utils.h"

/* use both functions */

file2.c

#define USE_COPY_FILE
#include "utils.h

/* use only copy_file */

ただし、この方法の問題点は、ユーティリティが導入されるにつれて見苦しくなることです。このような関数が 10 個あると想像してみてください。これらの関数が 7~8 個必要な場合は、インクルードの前に 7~8 行の定義が必要です!

もちろん、別の方法としてDONT_USE_*、関数を除外するタイプのマクロを使用することもできますが、その場合も、これらのユーティリティ関数をほとんど使用しないファイルに対して多くの定義が必要になります。

どう見てもエレガントじゃない。

私の質問は、どうすれば自分のライブラリの内部にある関数を複数の翻訳単位で使用し、外部リンケージを避けることができるでしょうか?

4

2 に答える 2

3

static inlineの代わりに関数をマークするstaticと、警告が消えます。現在のソリューションのコード膨張については何もしません。関数の少なくとも1つのコピーを、それを使用する各TUに配置しますが、これは引き続き当てはまります。Oliはコメントの中で、リンカーはそれらをマージするのに十分賢いかもしれないと言っています。そうではないと言っているわけではありませんが、期待しないでください:-)

TUごとに複数のコピーを取得できるように、コンパイラーに関数への呼び出しを実際にインライン化するように促すことで、肥大化をさらに悪化させる可能性があります。inlineしかし、それはありそうにありません。GCCはキーワードのその側面をほとんど無視します。独自のルールに従って、コールをインライン化するかどうかを決定します。

これは基本的に、移植可能にできる最善の方法です。標準Cには、特定のTU(ユーザー)のPOVからではなく、他のTU(ユーザー)のPOVから外部にあるシンボルを定義する方法はありません。標準Cは、ライブラリが何であるか、TUがいくつかのステップでリンクされる可能性があるという事実、または静的リンクと動的リンクの違いを実際には気にしません。したがって、ライブラリのユーザーに干渉する可能性のある外部シンボルを使用せずに、関数をTU間で実際に共有する場合は、GCCや静的ライブラリまたはdll形式に固有の処理を実行して、シンボルを一度削除する必要があります。ライブラリは構築されますが、ユーザーがライブラリにリンクする前に作成されます。

于 2012-05-24T10:03:14.753 に答える
2

ライブラリを通常どおりリンクして、これらの関数をグローバルにし、後でローカライズできます。

objcopyグローバル シンボルを取得してローカルにすることができるため、リンクすることはできません。また、シンボルを削除することもできます (関数は残り、それへの解決された参照は解決されたままになり、名前だけがなくなります)。

objcopy -L symbolローカライズしsymbolます。何回も繰り返すことができます-L
objcopy -G symbolグローバルを維持symbolしますが、他のすべてをローカライズします。それを繰り返すこともでき、指定したすべてのものをグローバルに保持します。

そして、Oli Charlesworth がコメントで参照したこの質問への回答を繰り返していることがわかりました。

于 2012-05-24T09:35:45.893 に答える