0

関数を含む1 つの翻訳単位と、main()メインのない別の TU があります。私が 2 番目のものしか制御できず、1 番目のものには触れられないとします。

ここでは説明しませんが、実行前にいくつかのコードを実行できるようにしたいと考えていますmain()。関数呼び出しでグローバル変数を初期化することでこれを実行できることは知っていますが、これを非表示にしたい-マクロをできるだけ使用しないでください(あえてマクロを使用しないと言いますか?おそらく不可能です.C++には適切な静的ブロックがありません) )

これを行うためのエレガントな、またはそれほど醜くない方法は何でしょうか? より明確にするために、この機能を一度だけ動作させるだけでなく、複数回使用できるようにするものを探しています。私はそれを次のようにしたい:

// ... at global scope ...
static {
    // my code here
}

PS: この質問は関連していますが、静的クラス メンバーの初期化に関するこの質問と同じではありません。また、C++ では実行できないというこの主張を明確に反証したいという願望にも動機付けられています。

注: はい、私は静的な初期化順序の大失敗について知っています。それを思い出させる必要はありません...そして、それをバイパスする何かを求めているわけではありません。明らかにコードを静的に実行するには、ある程度の慎重さが必要です。

4

2 に答える 2

5

静的な初期化順序の大失敗を楽しんでください:

int f(/* whatever args you want*/)
{
    // code to be ran before main()
    return 42;
}

static int _ignore = f(/*...*/);

他の場所で使用されていない場合、コードが呼び出されない場合があることに注意してください (別名「optimized out」)。そのようなケースの 1 つは、TU が静的ライブラリにコンパイルされる場合です (その後、未使用の変数とコードが実行可能ファイルに取り込まれない可能性があります)。(E.マスコフスキーのメモ)。

于 2015-12-18T16:30:04.060 に答える
2

これは、これまでのところ私が思いつくことができる最高のものです。動作しますが、実装はちょっとお粗末です。

使用法

あなたが書く場合:

STATIC_BLOCK {
    std::cout << "Hello static block world!" << std::endl;
}

このコードは、main(). ただし、開始std::cout前に書き込むことmain()は、実際にはそれほど優れたアイデアではないことに注意してください。

ノート:

  • 静的ブロック コードを中かっこで囲む必要があります(末尾のセミコロンは必要ありません。@KlitosKyriacou の提案に感謝します)。
  • 中括弧を使用しない場合は、わかりにくいエラー メッセージが表示されます。
  • C++ では、静的コードの相対的な実行順序は保証されません

実装

静的ブロックの実装には、ダミー変数が含まれます。他のダミー変数 (たとえば、別の静的ブロックまたはその他の場所) と衝突しないようにするために、マクロ機構が少し必要です。

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)

#define STATIC_BLOCK_IMPL2(function_name,var_name) \
static void function_name(); \
static int var_name __attribute((unused)) = (function_name(), 0) ; \
static void function_name()

#define STATIC_BLOCK_IMPL1(prefix) \  
    STATIC_BLOCK_IMPL2(CONCATENATE_FOR_STATIC_BLOCK(prefix,_fn),CONCATENATE_FOR_STATIC_BLOCK(prefix,_var))

#define STATIC_BLOCK STATIC_BLOCK_IMPL1(EXPAND_THEN_CONCATENATE(static_block_,__COUNTER__))

ノート:

  • コンパイラがサポートしていない場合__COUNTER__(標準の一部ではなく標準の拡張であるため) __LINE__、 を使用できます。これも機能します。GCC と Clang のサポート__COUNTER__
  • __attribute__((unused))属性は言語に組み込まれていますが、別のコンパイラ拡張機能です。たとえば、この議論を参照してください。ドロップすると警告が表示されます。
  • コードは C++98 (コンパイラの拡張機能を無視) です。つまり、最新の C++ コンストラクトをサポートする必要はありません。残念ながら、これは C とは見なされません (初期化子は定数でなければなりません)。

SCOPE_EXIT元々は Andrei Alexandrescu のトリックに触発されました。

于 2015-12-18T16:32:08.747 に答える