2012-10-15 20 views
4

這裏是用例:在編譯時從目標文件中替換符號。例如交換主

我有一個.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. 編譯爲u西伯利亞烏拉爾鋁業公司。

我所針對的環境是一個使用g ++的linux環境。

+0

我可能會推薦搜索/學習有關C++自動化單元測試?這是一個公平的問題,但聽起來你要重大改革。 – djechlin

+0

感謝您正確設置您的問題的格式。我誠實地感謝它。 –

+0

爲什麼不保留斷言並使用NDEBUG宏在編譯時刪除它們? –

回答

3

我討厭回答我的問題,但這裏是我結束了在g++手冊頁深尋找解決辦法,我測試過它,它的工作原理是什麼我會想要它...

g++-D標誌,它允許您在編譯目標文件時定義宏。我知道你在考慮「呃宏」,但是聽我說...你可以使用宏定義來有效地重命名一個符號。就我而言,我可以運行以下命令來生成我的學生代碼的目標文件,而不使用它們的主文件:g++ -D main=__students_main__ main.cpp -c -o main.nomain.o

這會創建一個對象文件,其int main定義爲int __students_main__。現在不一定可以直接調用它們,因爲它們可以將主要定義爲int 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 

對於我而言,我想創造會自動地(ISH)完成這個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 [email protected] 

%.o: %.cpp 
     $(CC) $(CFLAGS) -c $^ 
+1

預處理器方法存在輕微的風險,因爲它會替換任何匹配的令牌,而不僅限於函數名稱。在UNIX上使用* objcopy *來重命名符號的替代方法:'objcopy --redefine-sym main = __ students_main__ $ @'(或者基於文件名的計算替換?)。 –

+0

太棒了! -D重命名技巧也適用於gcc。它允許我用主功能在應用程序代碼周圍封裝具有main()函數的模糊代碼,並調用原始主體進行模糊化。 – rockdaboot

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 

希望這有助於!

+0

也許我沒有正確閱讀,這是如何防止''重複的符號_main「'? –

+0

我的不好,我想我從另一個角度理解了這一點。抱歉。 –

0

在編譯時無法做到這一點。你需要一個鏈接時間解決方案。 (或者使用預處理器)。

在這兩種情況下,您可能都想分開「常規」和「測試」符號,並選擇性地將它們的源文件包含在編譯中或鏈接中的對象文件中。

雖然我寧願使用單元測試框架或至少NDEBUGassert() s。

*如:

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

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

更新:我剛剛意識到:你專門找了一個解決方案,其中主testing.o主要會覆蓋main.o的(右?)。我會保留這個答案併爲「覆蓋」解決方案添加另一個答案。

1

您可以使用ld *的--allow-multiple-definition選項:

[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 

*:在您自己的風險:)

+0

+1這完成了我想要的任務,但是我的學生經常會有代碼無法編譯,包括由於重複定義的符號而導致的錯誤(這是一個介紹課程),我仍然想要表達這些錯誤。此外,編譯可能會通過makefile發生,我不一定會喜歡文件的非確定性排序以導致編譯輸出的後果。 –

+0

es,那些是我想到的風險。也許你可以通過首先連接其他所有東西來緩解這個問題,但對於我的口味來說,這仍然太過駭人聽聞。 – aib