2010-08-13 36 views
15

我覺得,每次我讀一個C或C++程序時,它的一半或更多隻是宏。我明白,宏可能很酷,但它們很難跟蹤,調試等。更不用說大多數編程語言甚至沒有定義像宏一樣的東西(雖然Perl6會有類似的東西)。過度依賴宏

我個人總是找到一種方法來寫我的代碼,而不使用宏,無論是與模板,多重繼承等。我甚至覺得我不是一個好的程序員,因爲所有的專業人員使用宏,我嘗試儘可能地避免它們。

問題是,有沒有宏無法解決的問題?宏是否最終是好的/壞的做法?我應該何時考慮使用宏?

+0

那麼,你不能在C中使用模板,所以這是一個原因。 – detly 2010-08-13 09:07:38

+7

如果您覺得「所有專業人員都使用宏」,您一直在閱讀什麼代碼?我知道的所有理智的C++代碼都很難避免它們。在C中,它有點不同,因爲你沒有多種選擇。順便說一句,爲什麼這個標籤C呢?你的問題*聽起來*非常特定於C++,提及模板和繼承。你知道,C和C++是不同的語言。 – jalf 2010-08-13 10:23:30

+0

@jalf說的 - 他們可能有一些相似之處,但是當涉及代碼生成時,他們完全不同的行星。例如,[X-macro/table generation idiom](http://stackoverflow.com/questions/2927245/looking-for-a-good-explanation-of-the-table-generation-macro-idiom) – detly 2010-08-13 10:37:29

回答

16

是的,這是一個。當您需要將跟蹤代碼添加到您的程序中時,一種配置包含它而另一種完全省略,您必須使用宏。

喜歡的東西:

#ifdef WITH_LOGGING 
    #define LOG(x) DoLog(x) 
#else 
    #define LOG(x) 
#endif 

現在你用這種方式:

LOG(L"Calling blahblahblah with " + getSomeStringHardToCompute()); 

,並與WITH_LOGGING配置你的代碼否則它完全省略 - 即使不存在在二進制中,因此

  • 它並不能幫助別人分析程序
  • 你會得到一個更小的二進制
  • 程序不會浪費時間在FO所有
  • 記錄編譯器可以產生更好的優化代碼。
+0

還經常用於添加在調試模式下運行但不在發佈模式代碼中運行的代碼。 – 2010-08-13 08:03:07

+0

這是一個例子,一個宏有用,但不是沒有宏的情況下根本無法完成的一個例子。例如,如果你明白我的意思,你可以在DoLog()的每個調用周圍放一個#ifdef塊。 – thomasrutter 2010-08-13 08:03:42

+1

這是模板或內聯函數。不需要宏。 – Basilevs 2010-08-13 08:03:54

-1

問題是,有沒有宏不能解決的問題?

是宏最終是好/後退的做法?我應該何時考慮使用宏?

在不支持或兌現inline關鍵字語言,宏重複使用代碼的好方法,但同時避免任何代碼的函數調用是緊密環繞足夠的開銷爲此做出巨大的改變。

您對代碼中散佈着宏的咆哮可能是有道理的。確實很難調試,並且在某些情況下需要閱讀。但是,在非常少量的情況下,這些優化確實是有保證的。

請注意,從C99開始,C現在可以使用inline關鍵字來執行明確的內聯函數,這可以減少對宏的需求,甚至可以減少對has advantages over using macros的需求。

+2

挑剔的是,你需要包含宏和條件編譯的宏,並且在許多框架中,如果禁用了日誌記錄,就可以避免記錄成本。 – 2010-08-13 08:07:47

+1

同意David。有些問題只能用宏來精確地解決(優雅地說是一個相對術語)。 – 2010-08-13 08:17:14

+0

不,因爲有和沒有宏的C++是圖靈完整的? :) 但是有很多情況下,只有宏(不是模板或內聯)允許避免代碼重複。我同意,儘可能避免宏應該是一個意圖,但誠實的答案必須是「是」。 – user396672 2010-08-13 09:54:40

8

你一直在看一些不好的C++代碼。我用宏的地方僅限於:

  • 頭衛士
  • 非常偶然的條件編譯
  • 一般例外拋出宏觀
  • 一般的調試/日誌輸出宏

我不我認爲這四點是可以避免的。

+3

特別是因爲記錄/投擲你一般希望自動提取文件名和行號。 – 2010-08-13 14:01:38

-4

編程語言宏適用於所有宏都適用的內容:避免一遍又一遍地輸入相同的內容。所以,如果你發現自己在很多地方都寫了相同的代碼,爲什麼不從它製作一個宏呢?尤其是如果你正在編寫一個庫,使用宏可以讓試圖使用這個庫的人更容易。看看幾乎所有的GUI工具包(Qt就是一個例子)。它們都廣泛使用宏。

+2

這就是我們使用函數的原因,而不是使用宏的原因。 – 2010-08-13 08:04:48

+0

這不是宏的一個很好的理由有更安全的解決相同的問題(功能)更優雅的解決方案 – 2010-08-13 08:18:46

+0

當一個函數不能做到這一點,當你應該達到預處理器宏 – TerryP 2010-08-14 17:18:00

9

斯科特Myer百貨公司有效的C++直 - > 1

鑑於consts和內聯的可用性,您的預處理器需求減少,但它並沒有完全消除。當你可以放棄#include時,這一天遠遠不夠,#ifdef /#ifndef在控制編譯中繼續扮演重要角色。現在還沒有時間退休預處理器,但你應該明確地計劃開始給它更長,更頻繁的休假。

1

當你想在預處理過程中生成代碼時,宏也是很有用的。儘管可以使用模板避免這種情況(請參閱此問題和討論 - Are C++ Templates just Macros in disguise?),但如果它可以使用戶的生活變得更加輕鬆,那麼可以使用宏 - 請參閱'googletest'項目(https://github.com/google/googletest/)如何有效地使用宏。您顯然不想使用宏來生成需要調試的代碼,而是使用模板。

4

調試行爲可以通過常量標誌或調試功能來控制。所以這裏是我不可避免的列表:

  • 多重包容性保護。
  • 宏是符號串化的唯一方法。 assert宏,緊湊的實現const string & stringify(枚舉類的值);

例子:

const char* stringify(enum category value) 
{ 
    #define c(x) case x: return #x; 
    switch(value) { 
     c(CIRCLE) 
     c(RECTANGLE) 
     c(TRIANGLE) 
     default: return "UNKNOWN"; 
    } 
    #undef c // the most important part 
} 
0

我認爲C++的模板和內聯函數使宏幾乎可以避免的。

宏的普遍存在可能是由於許多C++程序員曾經是C程序員。這樣的人可能會熟練使用宏(因爲它有時真的是純C中最好或唯一的解決方案),並且如果他們已經知道如何解決問題,則可能沒有意識到學習更復雜的C++功能。至少在開源世界中,有許多C轉換器,所以你自然會遇到C範例。如果你避免了這種功能,我認爲你不是一個糟糕的程序員,很多人就像GOTO一樣。

C(因此C++)是一種非常靈活的編程語言。這很棒,因爲每個人都可以發展自己獨特的風格,並以多種不同的方式解決大多數問題。但是,這也可以被認爲是一個問題。在我看來,這不是一個應該由語言來解決的問題,而應該通過建立公約來解決。

C++中有許多功能可以安全地忽略。也許有怪異的特殊場合這樣的功能真的是最好的方法,但在大多數情況下,你可以生活在沒有:

  • 朋友類
  • GOTO語句 多。

國際海事組織,高級C程序員應該至少能夠流利地閱讀它們 - 但我希望一個好的程序員仔細考慮何時以及如果使用臭名昭着的功能。

0

有許多問題,我無法解決沒有宏。 例如,串行化/一些結構的反序列化

 
#define STRUCT_DESCRIPTION structname(MyStruct) member(int,a) member(double,b) member(long, c) 
#include "declare_serializable_struct.h" // declares struct itself and generates serialization/deserializaton code 
#undef STRUCT_DESCRIPTION 

(BOOST_PP_SEQUENCE也可以使用) 另一個例子 - 使用消息映射分派消息,即,產生這樣的開關:

 
switch(message_type) 
{ 
case msg1: on_msg1(msg); break; 
case msg2: on_msg2(msg); break; 
... 
} 

和生成處理程序的方法聲明on_msgX(msg)在同一時間使用一些消息描述表(「地圖」)

就個人而言,我儘量避免宏,但我沒有成功我這樣。

然而,lambda表達式中的C++ 0x允許內嵌任意代碼爲「用戶或庫定義languge報表」這樣的foreach循環,使宏觀領域失去了顯著部分:)

0

也許這是一個有點偏離主題,但我正在閱讀post關於如何減少編譯時間。作者觀察到,在一個單獨的文件中包含每行代碼和一個宏(是的,這很醜),通過強大的比例(兩個參數約爲70%)減少了編譯時間和大小。

任何解釋?

+0

作者帖子有一些解釋,但我主要將它歸結爲:(1)錯誤的編碼實踐 - 爲什麼每個源文件似乎都包含每個標題,而不僅僅是與其相關的標題?和(2)Windows上的文件系統訪問速度非常慢。 – 2010-08-13 09:38:36

+0

是的,我的問題並不準確,但特別令我驚訝的是二進制文件大小的減少。 爲什麼包含太多的頭部比需要的是影響文件的大小? – Nielk 2010-08-13 09:48:27

0

宏是有條件編譯的解決方案(由ifdefifndef)。下面是示例:

1)

#ifndef MY_HEADER_HPP 
#define MY_HEADER_HPP 

//... 

#endif 

2)

#ifdef __cplusplus 
#define BEGIN extern "C" { 
#define END } 
#define NULL (0); 
#else 
#define BEGIN 
#define END 
#define NULL ((void*)0); 
#endif 

//------------------------- 

BEGIN 

void my_function(char* str); 

END 

//------------------------- 

void my_function(char* str) 
{ 
    if(str != NULL) 
    { 
     //... 
    } 
} 

但是內聯函數模板替換宏其它用途在C++中。

0

我傾向於儘可能避免使用宏,因爲它們有明顯的安全/調試問題,但有時候宏會提供語言中沒有其他功能優雅的東西,在這種情況下,我更喜歡使用宏只是因爲它使我的生活(以及我的開發人員)更加輕鬆。

例如,我已經創建了一個Enum類,它包裝枚舉在struct(範圍),並增加了一些功能:迭代

  • 可能性(這意味着該值的順序)
  • 轉換爲字符串(方便讀取/寫入文件,寫入日誌)

爲了創建枚舉,我使用了一個宏,它會自動生成轉換器(往返)和向量進行迭代。

當然,我可以沒有一個,畢竟宏只是代碼生成。但是如果沒有人,就意味着違反DRY,並且在我自己的小偏好「DRY」>「不要使用宏」。因爲一旦調試宏是安全的,而DRY違規是維護的噩夢。

現在,我儘快發現如何不違反DRY就會拋棄這個宏。想法顯然是受歡迎的...和外部腳本是不是更好;)

我2美分。

0

我儘量避免使用宏,但爲了擴大調試範圍,在調試時我還沒有找到打印文件名,函數名和行號的方法。

我通常有一個名爲DebugLog.h一個頭文件下面的宏

#define DEBUG(debugMessage) \ 
    printf("%s | %s [%d] - %s\n", __FILE__, __PRETTY_FUNCTION___, debugMessage); 

使用: DEBUG( 「測試」) 將輸出類似:

main.cpp | foo(void)[20] - Test 

您可以調整C++的宏和其他調試語句。也可以修改宏以將結果字符串發送到記錄器。