2012-08-15 79 views
6

我有一個靜態C庫,可以使用不同的編譯時間選項(例如_BUILD_SMALL,_BUILD_FAST)來構建。它有一個功能如何別名C庫函數?

void Foo(void); 

我想使用基準測試工具的單個實例來測試庫的「小」和「快」版本。我不想使用.dlls。

如何鏈接到「小」和「快」庫並將函數名稱別名,以便我可以調用小版本和快速版本。理想情況下,會看起來像:

void benchmark(void) 
{ 
    FAST_Foo(); 

    SMALL_Foo(); 
} 

的更多信息:

庫可以用不同的優化選項-Os與-O3建成。此外,算法略有不同(即緩存值與始終查找值)。我想比較不同版本的大小與速度權衡。我希望單元測試和基準測試能夠以最簡單的方式在庫的兩個版本上運行。

+2

這個問題是非常非常明確。 – Falmarri 2012-08-15 20:36:13

+0

爲什麼不簡單地創建多個版本,並打開/關閉各種選項並對每個版本進行基準測試? – 2012-08-15 20:37:24

+0

如果兩個函數的命名相同(且不是靜態的),則不能將這兩個函數組合在一個鏈接中。 – mah 2012-08-15 20:42:39

回答

3

這僅僅是一個由@米哈爾戈爾諾 - (我用完的評論空間有)給出的方法的變化...

您可以創建一個包括以下形式的文件:

/* Automatically created file - do not edit or ugly dinosaur will eat you */ 
#ifndef PREFIX 
# define RENAME(f) 
#else 
# define RENAME(f) PREFIX ## f 
#endif 

/* list all the function and variables you want to rename here in one place */ 
#define func_foo RENAME(func_foo) 
#define func_bar RENAME(func_bar) 
/* ... many more ... */ 

#undef RENAME 

至少gcc允許你指定一個頭文件從選項-include rename.h命令行中包含(假設此文件被稱爲rename.h)。因爲您使用gcc類似選項(-O3Os),我假設您在此答案的其餘部分使用gcc。否則,如果你的C編譯器是合理的,你應該可以用類似的方法來完成它。

您可以輕鬆地創建庫的兩個甚至三個版本,可以在同一時間,如果你想(在這裏通過CFLAGS設置)被鏈接,通過提供C編譯器的不同選項:

CFLAGS += -include rename.h -DPREFIX=fast_ -D_BUILD_FAST -O3 -DBENCHMARKING 
CFLAGS += -include rename.h -DPREFIX=small_ -D_BUILD_SMALL -Os -DBENCHMARKING 
CFLAGS += -D_BUILD_FAST -O2 

如果你的庫的頭文件看起來很正規的,如果你聲明庫私有函數static,那麼很容易用很簡單的正則表達式來自動生成rename.h文件給你一些虛擬腳本來提取這些頭文件的功能。如果您使用make或類似的東西,這是一個自然的構建目標。所有全局變量也需要使用相同的方法重命名以允許同時使用。

有這個解決方案主要有三點:

  1. 醜陋重命名業務可以被隱藏在一個文件中,你不需要編輯實際源文件 - 尤其是你不需要的雜波源文件,但可以保持它們的清潔和易於閱讀。
  2. 如果您遵循一些簡單的原則(頭文件和頭文件將遵循編碼慣例將聲明所有全局變量和函數),則重命名可以很容易地自動地
  3. 沒有理由使基準比較繁瑣的需要運行測試程序多次(如果你很懶,我和猛烈,因爲我不喜歡宋衍濤任務,這是相關的 - 我知道很多人不關心,這有點偏愛)。
+0

非常好,適合問題,謝謝! – 2012-08-21 11:35:49

2

一種方法是:爲兩者保留相同的名稱並根據編譯時間選項設置適當調用。

ifdef SMALL_FOO 
void foo() { 

/* Small foo code */ 
} 
#endif 

ifdef BIG_FOO 
void foo() { 

/* Big foo code */ 
} 
#endif 

-d編譯期間設置SMALL_FOO/BIG_FOO

+0

我曾考慮過這個。目前的實施需要一些重複來做到這一點,但我認爲可以這樣做。 – 2012-08-15 23:13:57

0

你說你可以建立庫,改變編譯時間選項,爲什麼不編輯代碼來改變每個函數的名字。 (你會做兩個不同版本的庫。)

+0

編譯時選項傳遞給預處理器,代碼不會改變(反正這就是目標)。 – 2012-08-15 23:10:35

2

作爲一個快速的解決方案,你可以使用宏來裂傷函數名,如:

#ifdef FAST 
# define FUNC(x) FAST_##x 
#else 
# define FUNC(x) SLOW_##x 
#endif 

void FUNC(Foo)(); 

而目前隨着-DFAST圖書館與FAST_Foo會建造;沒有它,一個與SLOW_Foo。請注意,在實現部分中也需要使用FUNC()宏(並且每當您從庫中引用該函數時)以及#ifdef FAST在快速/慢速代碼之間切換。

只需不要在生產代碼中使用它。

+0

我還會添加一個'#ifdef ALIAS',當false會繞過上述和'#else'子句時'#define FUNC(x)x'。那麼你不需要在實現部分使用宏。 – 2012-08-15 22:06:29

+0

我在想這個。我正在尋找別名解決方案,但我曾考慮過構建特定的「mangled」庫函數,然後執行#ifdef FAST #define Foo FAST_Foo()#endif – 2012-08-15 23:12:54

+0

我同意重命名,但我個人不喜歡按建議的方式這裏。我不喜歡改變函數原型的顯示方式。你不能再使用正則表達式來搜索它們,一些處理C文件的工具會變得困惑,並且它使視圖混亂。我寧願在函數定義/聲明之前做。例如,在上面的例子中,說'#define Foo FUNC(Foo)'(使用你的'FUNC()'宏和'Foo()'函數,其中'FUNC() 。然後你可以擁有正常的功能原型。 – FooF 2012-08-21 03:35:55

2

如果您嘗試將兩個靜態庫鏈接到同一個可執行文件,鏈接行中列出的第二個庫將不會產生任何影響,因爲它提供的所有符號都已被第一個庫滿足。如果您提供了簡單的唯一包裝函數來調用Foo,它現在仍會失敗,因爲有多個定義。下面是一個例子:

/* x.c */ 
extern void Y_Bar(); 
extern void Z_Bar(); 
int main() 
{ 
    Y_Bar(); 
    Z_Bar(); 
} 

該主呼叫獨特包裝函數,其在liby.alibz.a提供。

/* y.c in liby.a */ 
#include <stdio.h> 
void Y_Bar() { 
    extern void Foo(); 
    Foo(); 
} 
void Foo() { 
    printf("%s\n", "that Foo"); 
} 

/* z.c in libz.a */ 
#include <stdio.h> 
void Z_Bar() { 
    extern void Foo(); 
    Foo(); 
} 
void Foo() { 
    puts("this foo"); 
} 

試圖鏈接可執行文件與-ly -lz將失敗。

最簡單的解決方法是構建兩個獨立的可執行文件。然後,您的基準驅動程序可以執行這兩個可執行文件來比較其相對性能

+0

我選擇這個作爲生產代碼最簡單的解決方案。 – 2012-08-15 23:25:08

0

也許你可以使用-D option when call gcc, like -D_FAST_, -D_SMALL_,或使用使當你能收到的輸入參數,如使用使CFG=FAST, make CFG=SMALL,makefile,您可以定義,當獲取參數FAST, link to FAST library