【问题标题】:When could macro make code more beautiful than function does? [closed]宏什么时候可以让代码比函数更漂亮? [关闭]
【发布时间】:2012-07-27 10:09:25
【问题描述】:

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

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

【问题讨论】:

  • 抱歉,机器人投票结束这个问题是主观的。
  • “机器人投票”是什么意思
  • @qiuxiafei,这可能是“got”的拼写错误
  • @SingerOfTheFall:我认为这是一个“但是”的错字:)。
  • 我怀疑他们的意思是“但投票”,我倾向于同意他们的观点。

标签: c++ c macros


【解决方案1】:

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

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

假设您要定义一个名为period 的枚举,其成员为onefivetenfifteenthirty。那么你就是这样做的:

  • 首先创建一个名为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_stringto_period 可以正常工作,无需任何修改!

--

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

【讨论】:

  • 谈到代码混淆...我觉得不好看
  • 这是一个杂牌。 Kludges 通常聪明但丑陋,这不能算作例外。
  • 我不会包含不同的标头,而是使用一个将不同的宏作为参数的宏:#define APPLY( MACRO ) MACRO(one) MACRO(two) 然后调用它enum X { #define ENUM_ITEM(x) x, APPLY( ENUM_ITEM ) #undef ENUM_ITEM };@987654343 @APPLY` 宏传递多个参数。假设您有一个发送field_idfield_content 和给定字段列表的消息协议。您可以让APPLY 定义映射:#define APPLY( M ) M(my_field,value,type)...
【解决方案2】:

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

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

宏在 C++ 中的主要用途是控制编译。比如:

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

#ifdef OS_WINDOWS
    //windows specific code
#

【讨论】:

  • 宏在 C 语言中也非常有用。可以控制相同的代码以支持不同版本的内核(例如示例)。
【解决方案3】:

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

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

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" \
)
"...";

【讨论】:

    【解决方案4】:

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

    要做好,宏必须:

    • 要简单。
    • 永远不要模棱两可
    • 永远不要破坏代码结构(我见过像 #define MACRO } someCode { 这样的宏,太糟糕了)
    • 不能抛出异常或类似的东西,基本上,它决不能让调试变得更难
    • 有一个能清楚解释含义的名称
    • 必须有一个确实充分的理由来使用宏而不是内联函数,例如编译控制或标头保护。

    【讨论】:

      【解决方案5】:

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

      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
      }
      

      【讨论】:

        猜你喜欢
        • 2016-04-19
        • 2018-11-15
        • 1970-01-01
        • 2010-10-03
        • 2020-05-14
        • 2013-04-15
        • 1970-01-01
        • 1970-01-01
        • 2019-08-24
        相关资源
        最近更新 更多