1

确切地说,宏对于编程语言来说不是必需的。例如,Java 在没有宏的情况下工作得很好。通常宏使代码更清晰、更短,同时也更危险。

那么,使用宏的最佳方法是什么?让我们用代码说话。

4

5 回答 5

5

使用宏,您可以为这样的问题编写一个漂亮的解决方案:

  • 定义一个枚举,以便它的值可以转换为它的字符串表示形式,反之亦然。

假设您要定义一个名为的枚举,period其成员为onefive、和。那么这里是你如何做到的:tenfifteenthirty

  • 首先创建一个名为的头文件period_items.h

    //period_items.h
    
    //Here goes the items of the enum
    //it is not a definition in itself!
    E(one)
    E(five)
    E(ten)
    E(fifteen)
    E(thirty)
    
  • 然后创建另一个名为period.h的头文件:

    //period.h
    #include <string>
    
    //HERE goes the enum definition!
    enum period 
    {
       #define E(item)  item,
         #include "period_items.h" //it dumps the enum items in here!
       #undef E
       period_end
    };
    
    period to_period(std::string const & name)
    {
       #define E(item)  if(name == #item) return item;
         #include "period_items.h"
       #undef E
       return period_end;
    }
    

现在您可以简单地包含period.h和使用to_period函数。:-)

您还可以将此功能添加到period.h

std::string to_string(period value)
{
    #define E(item)  if(value == item) return #item;
        #include "period_items.h"
    #undef E
    return "<error>";
}

现在,你可以这样写:

#include "period.h"

period v = to_period("fifteen"); //string to period
std::string s = to_string(v);  //period to string

为什么这个解决方案很漂亮?

因为现在如果您想向枚举添加更多成员,您所要做的就是将它们添加period_items.h为:

    //period_items.h

    //Here goes the items of the enum
    //it is not a definition in itself!
    E(one)
    E(five)
    E(ten)
    E(fifteen)
    E(thirty)
    E(fifty)       //added item!
    E(hundred)     //added item!
    E(thousand)    //added item!

你完成了。to_string并且to_period可以正常工作,无需任何修改!

--

我将此解决方案从我的解决方案中提取到另一个问题,发布在这里:

于 2012-07-27T10:22:34.097 回答
3

我认为最好的方法是使用inline

您可以获得宏的所有好处 + 所有编译时检查

宏在 C++ 中的主要用途是用于控制编译。就像是:

#ifdef DEBUG:
    //print some debug information
#endif

或者

#ifdef OS_WINDOWS
    //windows specific code
#
于 2012-07-27T10:10:57.817 回答
1

在我个人看来,一个好的宏是非常罕见的。我尽量避免它们,因为它们中的大多数更像是一颗定时炸弹。

要做好,宏必须:

  • 简单点。
  • 永远不要模棱两可
  • 永远不要破坏代码结构(我见过像这样的宏#define MACRO } someCode {,这很糟糕)
  • 基本上,不能抛出异常或类似的东西,它决不能使调试更难
  • 有一个清楚地解释含义的名字
  • 必须有一个非常好的理由使用宏而不是内联函数,例如编译控制或标头保护。
于 2012-07-27T10:15:16.520 回答
1

我只在没有其他方法的地方使用宏。

一个例子是从错误值到字符串的简单映射,例如,而不是

switch(code) {
    case ERR_OK: return "ERR_OK";
    case ERR_FOO: return "ERR_FOO";
    :

我使用一个简单的宏,比如

#define CASE_STR(x) case x: return #x

所以我可以将其简化为

switch(code) {
    CASE_STR(ERR_OK);
    CASE_STR(ERR_FOO);
    :

但是,这些情况通常更适合调试。

此外,我使用 boost 预处理器套件编写了一个 GLSL(OpenGL 着色语言)循环展开一次,然后可以使用类似

const char *shader = "#version 120\n"
"..."
GLSL_UNROLL_FOR("int i",0,10,\
    "foo += i\n" \
)
"...";
于 2012-07-27T10:32:07.010 回答
1

这是一个示例(易于维护)。

enum id {
#define ITEM(id, s) id,
# include "foo.itm"
#undef ITEM
    nbItem
};

static char const *const s[] = {
#define ITEM(id, s) s,
# include "foo.itm
#undef ITEM
}
于 2012-07-27T10:32:50.763 回答