特別なコンパイラ コマンドを使用すると、シンボルをweakとして宣言できます。ウィキペディアによると:
弱いシンボルは、オブジェクト ファイルまたは動的ライブラリ内のシンボル定義であり、他のシンボル定義によってオーバーライドされる可能性があります
弱いシンボルが必要なシナリオやアプリケーションは何ですか? 典型的なユースケースは何ですか?
特別なコンパイラ コマンドを使用すると、シンボルをweakとして宣言できます。ウィキペディアによると:
弱いシンボルは、オブジェクト ファイルまたは動的ライブラリ内のシンボル定義であり、他のシンボル定義によってオーバーライドされる可能性があります
弱いシンボルが必要なシナリオやアプリケーションは何ですか? 典型的なユースケースは何ですか?
組み込み開発では、たとえば割り込みポインターのベクトルがある場合、関心のない割り込みのデフォルト ハンドラーを取得するために弱いリンクを使用できると非常に便利です。
これは、空のハンドラーを (1 回) 定義し、必要な割り込みポインターごとに 1 つの新しい適切な名前のシンボルを導入することで機能します。これは、デフォルトのハンドラーに弱くリンクされています。
次に、ベクトルはこれらのシンボルで満たされ、同じ(適切な)名前を使用してそのうちの1つを実装することを決定するまで、すべて同じ実際のコードを指し、コードが弱いリンクを「圧倒」し、割り込みテーブルにインストールされるコード。
これは、C とアセンブリが混在して実装されることがよくありますが、C 擬似コードを使用すると、次のようになります。
static void placeholder_isr(void)
{
}
/* Introduce properly-named function pointers, with weak linking.
* NOTE: This syntax is completely fictional as far as I know.
*/
void (*timer1_isr)() = placeholder_isr __attribute("weak linking");
void (*timer2_isr)() = placeholder_isr __attribute("weak linking");
void (*usart1_isr)() = placeholder_isr __attribute("weak linking");
void (*usart2_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
/* Declare the table of interrupt handlers. */
static void (*isr_table)[] = {
timer1_isr,
timer2_isr,
usart1_isr,
usart2_isr,
dma1_isr,
dma2_isr,
} __attribute("isr vector"); /* Attribute to place it where it needs to go. */
次に、必要に応じて独自の関数を実装できます。
void timer1_isr(void)
{
/* Handler ISR from timer1. */
}
他に何も変更する必要はなく、「ただ機能する」だけです。もちろん、あなたの名前が上記の「サポートコード」が期待するものである限り。
弱いリンクの用途の 1 つは、C++ 標準で置き換え可能な関数を実装することです。すなわち:
void *operator new(std::size_t);
void *operator new(std::size_t, std::nothrow_t const &) noexcept;
void *operator new[](std::size_t);
void *operator new[](std::size_t, const std::nothrow_t&) noexcept;
void operator delete(void *) noexcept;
void operator delete(void *, std::nothrow_t const &) noexcept;
void operator delete[](void *) noexcept;
void operator delete[](void *, std::nothrow_t const &) noexcept;
これらは実装によって提供される必要がある関数ですが、プログラムがそれらを実装する場合、プログラムの実装は実装のバージョンを置き換えるかオーバーライドします。これは、弱いリンケージを介して簡単に実装できます。
典型的かつ日常的な使用例は、インライン関数とテンプレート関数です。
たとえば、次のようにコンパイルした場合のコードのこの部分g++ -shared -fPIC
:
extern void a();
inline void foo() { a(); }
void bar() { foo(); }
void baz() { foo(); }
シンボル bar と baz は T (通常) by としてマークされnm
、foo は W - weak としてマークされます。
(つまりmn -C ./a.out
)
根拠:
インライン関数とテンプレートはヘッダーで定義でき、通常はソースのさまざまな部分で複数回定義され、最終的にアクティブなままになるのは 1 つだけです。
Weak としてマークされていない場合、複数の "foo" シンボルが衝突するか、コンパイラがインライン化を無効にできません。
weak属性により、宣言はグローバルではなくウィーク シンボルとして出力されます。これは主に、ユーザー コードでオーバーライドできるライブラリ関数の定義に役立ちますが、関数以外の宣言でも使用できます。ウィーク シンボルは、ELF ターゲットでサポートされています。また、GNU アセンブラーとリンカーを使用する場合は a.out ターゲットでもサポートされています。
弱い.c
extern void foo() __attribute__((weak));
int main() {
if (foo) foo();
}
foo.c
void foo() {
printf("in foo.\n");
}
強い.c
extern void foo() ;
int main() {
if (foo) foo();
}
コンパイル中
$ cc weak.c // Compiles OK
$ cc strong.c // undefined reference to `foo'
「foo」が弱いと宣言されている場合、その定義を省略したり、「リンク時バインディング」を特徴とする別のライブラリに置き換えたりすることができます。リンカーは、未定義の弱いシンボルに対して 0 を埋めます。
通常、コードの別の部分で関数定義をオーバーライドできるようにする場合は、弱いリンクを使用します。これは通常、ライブラリを使用する場合にカスタム関数でオーバーライドできるデフォルトのエラー ハンドラなどを指定するライブラリの場合です。