背景
最近いじっているCのソフトに、マルチプラットフォーム対応の静的なプラグイン機構1をつけたくて、明示的に呼ばずに初期化処理を走らせる方法をいろいろ調べてみました。
なおDLLやSOなどはエントリポイントがあるのでそちらを使ってもよいと思います。
解
- C++に妥協し、static変数のコンストラクタを使う
-
__attribute__((constructor))
を使う(gcc拡張) -
.CRT$X??
セクションに配置する(VCのCRT機能)
Stack Overflowの以下の回答がわかりやすいです。
c - __attribute__((constructor)) equivalent in VC? - Stack Overflow
コードも抜粋しておきます。public domainなので、使いやすくてよいですね。
// Initializer/finalizer sample for MSVC and GCC/Clang.
// 2010-2016 Joe Lowe. Released into the public domain.
# include <stdio.h>
# include <stdlib.h>
# ifdef __cplusplus
#define INITIALIZER(f) \
static void f(void); \
struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
static void f(void)
# elif defined(_MSC_VER)
#pragma section(".CRT$XCU",read)
#define INITIALIZER2_(f,p) \
static void f(void); \
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
__pragma(comment(linker,"/include:" p #f "_")) \
static void f(void)
#ifdef _WIN64
#define INITIALIZER(f) INITIALIZER2_(f,"")
#else
#define INITIALIZER(f) INITIALIZER2_(f,"_")
#endif
# else
#define INITIALIZER(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
# endif
static void finalize(void)
{
printf( "finalize\n");
}
INITIALIZER( initialize)
{
printf( "initialize\n");
atexit( finalize);
}
int main( int argc, char** argv)
{
printf( "main\n");
return 0;
}
参考サイト(調査時の閲覧順)
-
main() の前に関数を呼ぶ - bkブログ
gcc関係だとおそらく真っ先にヒットする解説ページ。arでリンクされなくなる問題への対処法(--whole-archive
)も。 -
c - __attribute__((constructor)) equivalent in VC? - Stack Overflow
Stack Overflowでのやりとりとマルチプラットフォームのエレガントな回答 -
ブログズミ: C で main の前に関数コール
テスティングフレームワークiutestを作成されている方の調査記事。
iutestはインクルードのみで利用できるらしく、なかなか便利そう。 -
MSDN : CRT の初期化
VCの初期化の仕組み -
Common Function Attributes - Using the GNU Compiler Collection (GCC)
gccのオンラインマニュアル
-
既存コードに手を入れずに、作成したCソースファイルをビルドに含めるだけで、機能一覧に登録されるような機構。ユニットテスト作成時等にも便利。 ↩