私のソフトウェアには、通常の使用用に1つのメインがあり、単体テスト用に別のメインがあります。使用する「メイン」関数を指定するgccのオプションがあれば、私はそれが大好きです。
11 に答える
ここでの他の答えは非常に合理的ですが、厳密に言えば、あなたが抱えている問題は実際にはGCCの問題ではなく、Cランタイムの問題です。-e
フラグを使用して、プログラムへのエントリポイントを指定できますld
。私のドキュメントには次のように書かれています。
-e symbol_name
メイン実行可能ファイルのエントリポイントを指定します。デフォルトでは、エントリ名は「start」です。これは、crt1.oにあり、main()を設定して呼び出すために必要なグルーコードが含まれています。
start
つまり、必要に応じてエントリポイントをオーバーライドできますが、プログラムの実行前に必要なあらゆる種類のOS固有の処理を実行する可能性があるため、マシンで通常実行する予定のCプログラムではオーバーライドしたくない場合があります。あなたがあなた自身を実装することができればstart
、あなたはあなたが望むことをすることができます。
それらを別々のファイルに入れ、通常の使用用に1つの.cファイルを指定し、テスト用に1つの.cファイルを指定します。
または、#define
テストビルドを使用してコマンドラインでテストし、次のようなものを使用します。
int main(int argc, char *argv[])
{
#ifdef TESTING
return TestMain(argc, argv);
#else
return NormalMain(argc, argv);
#endif
}
int TestMain(int argc, char *argv[])
{
// Do testing in here
}
int NormalMain(int argc, char *argv[])
{
//Do normal stuff in here
}
マクロを使用して、1つの関数の名前をmainに変更できます。
#ifdef TESTING
#define test_main main
#else
#define real_main main
#endif
int test_main( int argc, char *argv[] ) { ... }
int real_main( int argc, char *argv[] ) { ... }
Makeなどを使用していると思います。main関数の異なる実装を含む2つのファイルを作成し、makefileで、残りのファイルに同じ依存関係を持つ2つの別個のターゲットを定義します。ただし、1つは「単体テストメイン」を使用し、もう1つは「通常のメイン」を使用します。 "。このようなもの:
normal: main_normal.c file1.c file2.c
unittest: main_unittest.c file1.c file2.c
「通常の」ターゲットがmakefileの先頭に近い限り、「make」と入力するとデフォルトで選択されます。テストターゲットを作成するには、「makeunittest」と入力する必要があります。
私は別のファイルを使用してテストビルドと本番ビルド用に作成する傾向がありますが、
int test_main (int argc, char*argv[])
と
int prod_main (int argc, char*argv[])
次に、メインとしてどちらかを選択するコンパイラオプション-Dtest_main=main
と-Dprod_main=main
今日も同じ問題が発生しました。m1.cとm2.cはどちらもメイン機能を持っていましたが、リンクしてそのうちの1つを実行する必要がありました。解決策:ユーザーSTRIPを使用して、コンパイル後、リンクする前に、そのうちの1つからメインシンボルを削除します。
gcc -c m1.c m2.c; strip --strip-symbol main m1.o; gcc m1.o m2.o; ./a.out
m2からメインを実行します
gcc -c m1.c m2.c; strip --strip-symbol main m2.o; gcc m1.o m2.o; ./a.out
m1からメインを実行します
ストリップなし:
gcc - m1.c m2.c
m2.o: In function `main':
m2.c:(.text+0x0): multiple definition of `main'
m1.o:m1.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
編集:ビリーは答えに私を打ち負かしました、しかしここにもう少し背景があります
より直接的には、mainは通常、標準ライブラリの機能です。呼び出すのmain
はCではなく、標準ライブラリです。OSはアプリケーションをロードし、制御をライブラリのエントリポイント(_start
GCC内)に移し、ライブラリは最終的にを呼び出しますmain
。これが、WindowsアプリケーションのエントリポイントがWinMain
通常ではなく、になる可能性がある理由です。組み込みプログラミングも同じようなものです。標準ライブラリがない場合は、ライブラリが通常提供するエントリポイント(特に)を作成する必要があり、任意の名前を付けることができます。
GCCツールチェーンでは、オプションを使用して、ライブラリのエントリポイントを独自のエントリポイントに置き換えることもでき-e
ます。(さらに言えば、ライブラリを完全に削除することもできます。)
自分で作る:
int main(int argc, char *argv[])
{
#if defined(BUILD_UNIT_TESTS)
return main_unittest(argc, argv);
#endif
#if defined(BUILD_RUNTIME)
return main_run(argc, argv);
#endif
}
が気に入らない場合はifdef
、mainのみを含む2つのメインモジュールを作成します。1つを単体テスト用にリンクし、もう1つを通常の使用用にリンクします。
intoを使用する場合LD "-e symbol_name"
(もちろん、symbol_nameがメイン関数です)、"-nostartfiles"
それ以外の場合もエラーが発生"undefined reference to main"
します。
まず、main
1つのコンパイルで2つの関数に名前を付けることはできないため、ソースが異なるファイルにあるか、条件付きコンパイルを使用しています。いずれにせよ、2つの異なる.oファイルを取得する必要があります。したがって、リンカーオプションは必要ありません。必要な.oファイルを引数として渡すだけです。
それが気に入らない場合は、動的に名前を付けた任意のオブジェクトファイルからdlopen()
プルするための凝ったことを行うことができます。main
これが役立つ状況を想像できます。たとえば、単体テストに体系的なアプローチを取り、それらをすべてディレクトリに配置すると、コードがディレクトリをウォークし、各オブジェクトファイルを取得して動的にロードし、テストを実行します。しかし、始めるために、おそらくもっと簡単なことが示されます。
#ifdef TESTING
int main()
{
/* testing code here */
}
#else
int main()
{
/* normal code here */
}
#endif
$ gcc -DTESTING=1 -o a.out filename.c #building for testing
$ gcc -UTESTING -o a.out filename.c #building for normal purposes
man gcc
-Dと-Uを見せてくれた
それらを使用するには個別に実行する必要がある場合がありますが、ldは、出力ファイル(エントリポイントを含む)の多くの側面を定義するスクリプトをサポートしています。ld