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__. 现在这不一定可以直接调用,因为他们可以将 main 定义为或使用andint main(void)的各种组合,但它允许我有效地编译出它们的函数。argcargv

最终编译如下:

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(ish),我觉得这与此讨论相关,所以我将发布我想出的内容:

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

我支持'djechlin 的建议。但是,如果您想要快速而肮脏的东西,这里有一个建议:

您可以定义一个宏并像这样包装您的函数调用,

#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 回答
0

在编译时不可能这样做。您需要一个链接时间解决方案。(或使用预处理器。)

在任何一种情况下,您可能都希望将“常规”和“测试”符号分开,并有选择地将它们的源文件包含在编译中,或者将目标文件包含在链接中。*

虽然我宁愿使用单元测试框架或至少NDEBUG对于assert()s.

*例如:

#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 将覆盖 main.o 的(对吗?)。我会保留这个答案并为“覆盖”解决方案添加另一个答案。

于 2012-10-16T01:44:59.353 回答