6

ユースケースは次のとおりです。

.cpp関数が実装されたファイルがあります。例として、次のようにします。

[main.cpp]

#include <iostream>

int foo(int);

int foo(int a) {
    return a * a;
}

int main() {
    for (int i = 0; i < 5; i += 1) {
        std::cout << foo(i) << std::endl;
    }

    return 0;
}

このファイルの関数に対してある程度の自動テストを実行したいのですが、テストを実行するには関数fooを置き換える必要がありmain()ます。できれば、このような別のファイルを作成して、そのファイルの上にリンクできるようにしたいと思います。

[mymain.cpp]

#include <iostream>
#include <cassert>

extern int foo(int);

int main() {
    assert(foo(1) == 1);
    assert(foo(2) == 4);
    assert(foo(0) == 0);
    assert(foo(-2) == 4);        

    return 0;
}

これを行うために元のファイルを変更することは(可能な限り)避けたいと思い.cppますが、これが不可能な場合の私のアプローチは次のとおりです。

  1. "(\s)main\s*\("==>の置換を行います"\1__oldmain\("
  2. いつものようにコンパイルします。

私が対象としている環境は、g++ を使用した Linux 環境です。

4

4 に答える 4

5

私は自分の質問に答えるのが嫌いですが、のマニュアルページの奥深くで見つけた解決策を次に示しますg++。テストしたところ、希望どおりに機能します...

g++-Dオブジェクトファイルのコンパイル時にマクロを定義できるフラグがあります。あなたが「マクロが悪い」と考えていることは知っていますが、聞いてください... マクロ定義を使用して、シンボルの名前を効果的に変更できます。私の場合、次のコマンドを実行して、メイン ファイルなしで生徒コードのオブジェクト ファイルを生成できますg++ -D main=__students_main__ main.cpp -c -o main.nomain.o

int mainこれにより、定義済みのオブジェクト ファイルが作成されint __students_main__ます。int main(void)main を asまたは と のさまざまな組み合わせでargc定義できたので、これは必ずしも直接argv呼び出すことができるわけではありませんが、関数を効果的にコンパイルすることができます。

最終的なコンパイルは次のようになります。

g++ -c -D main=__students_main__ main.cpp -o main.nomain.o
g++ -c mymain.cpp -o mymain.o
g++ main.nomain.o mymain.o -o mymainstudentsfoo.out

私の目的のために、これを自動的に(っぽい)達成するMakefileを作成したかったのですが、それがこの議論に関連していると感じたので、思いついたものを投稿します:

HDIR=./ # Not relevant to question, but we have headers in a separate directory
CC=g++
CFLAGS=-I $(HDIR)
NOMAIN=-D main=__student_main__ # The main renaming magic

.SECONDARY: # I forget exactly what this does, I seem to remember it is a hack to prevent deletion of .o files

cpp = $(wildcard *.cpp)
obj = $(patsubst %.cpp,%.o,$(cpp))
objnomain = $(patsubst %.cpp,%.nomain.o,$(cpp))

all: $(obj) $(objnomain)

clean:
        rm -f *.o *.out

%.nomain.o: %.cpp
        $(CC) $(CFLAGS) $(NOMAIN) -c $^ -o $@

%.o: %.cpp
        $(CC) $(CFLAGS) -c $^
于 2012-10-16T15:39:15.507 に答える
3

*の--allow-multiple-definitionオプションを使用できます。ld

[a.c]

#include <stdio.h>

int foo() { return 3; }
int bar() { return 4; }

int main(void)
{
    printf("foo: %i\n", foo());
    return 0;
}

[b.c]

#include <stdio.h>

int bar();

int main(void)
{
    printf("bar: %i\n", bar());
    return 0;
}

[shell]

$ gcc -Wall -c a.c
$ gcc -Wall -c b.c

$ gcc -Wl,--allow-multiple-definition a.o b.o -o foobar && foobar
foo: 3
$ gcc -Wl,--allow-multiple-definition b.o a.o -o foobar && foobar
bar: 4

*: ご自身の責任で :)

于 2012-10-16T02:24:35.840 に答える
0

コンパイル時にこれを行うことはできません。リンク時のソリューションが必要です。(または、プリプロセッサを利用するため。)

どちらの場合でも、「通常の」シンボルと「テスト用」のシンボルを分離し、それらのソース ファイルをコンパイルに含めるか、オブジェクト ファイルをリンクに含めるかを選択することをお勧めします。

単体テスト フレームワークまたは少なくともsNDEBUGを使用したいのですが。assert()

*例えば:

#ifdef TESTING
#include "main-testing.c"
#else
#include "main.c"
#endif

また

ifdef TESTING
OBJS += main-testing.o
else
OBJS += main.o
endif

更新: main-testing.o のメインが main.o をオーバーライドするソリューションを具体的に探していることに気付きました (そうですか?)。この回答を保持し、「オーバーライド」ソリューション用に別の回答を追加します。

于 2012-10-16T01:44:59.353 に答える
0

私はジェクリンの提案を支持します。しかし、手早く汚いものが必要な場合は、次の提案があります。

マクロを定義して、次のように関数呼び出しをラップできます。

#ifdef MYTESTING
#define ASSERTEQUAL(fn, parm, ret) \
assert( fn ( parm ) == ret )
#else
#define ASSERTEQUAL(fn, parm, ret) fn ( parm )
#endif

メイン関数では、次の呼び出し ASSERTEQUAL( foo, i, 4); を使用します。

次のコンパイル フラグを使用して、カスタマイズされたマクロの動作を有効にします。

-DMYTESTING

お役に立てれば!

于 2012-10-15T23:27:47.653 に答える