【问题标题】:If-else statement inside multi-line macro (C++)多行宏中的 if-else 语句 (C++)
【发布时间】:2014-03-12 13:54:40
【问题描述】:

其实我有两个问题。

  1. 这是“ if (ClassPtr1) && (ClassPtr2) ”检查它们是否不为空的正确方法吗?

  2. 我想知道是否可以将 if else 语句完全包含在多行宏中。我附上了示例代码。

    #define MACRO_NAME(object,expression){\
    Class1* ClassPtr1 = dynamic_cast<Class1*>(object);\
    Class2* ClassPtr2 = ClassPtr1->SomeMethod();\
    if (ClassPtr1) && (ClassPtr2)\
    {\
        try\
        {\
            //some code
        }\
        catch(...)\
        {\
            //some code
        }\
    }\
    else\
        return expression;\
    }
    

【问题讨论】:

  • 您缺少一些括号。除此之外,你测试过吗?尝试生成预处理代码,看看。
  • 另请注意,必须使用dynamic_cast 可能是糟糕设计的标志。把SomeMethod设为虚拟就不能解决吗?
  • 顺便说一句,当有多个语句并且需要宏中的块时,通常的模式是在do { ... } while (false) 块内(没有结束分号)。

标签: c++ if-statement macros


【解决方案1】:

1:不。您在检查之前使用ClassPtr1,如果它实际上为空,则这是未定义的行为。你需要先获取ClassPtr1,然后检查,然后用它获取ClassPtr2,然后检查。

2:是的,原则上没问题。宏是否是一个好主意是一个不同的问题。

【讨论】:

    【解决方案2】:

    1:可以用c++11关键字nullptr进行检查。

    2:是的,你可以!

    【讨论】:

    【解决方案3】:

    是的,它有效。唯一的问题是您缺少if 强制括号,因此一旦展开,它将无法按原样编译。

    此外,您必须在访问它们之前检查这两个指针,否则您将获得未定义的行为,正如@Sebastian Redl 所指出的那样。

    【讨论】:

      【解决方案4】:
      1. 语法不太正确;那将是if (ClassPtr1 &amp;&amp; ClassPtr2)if 的条件必须用括号括起来)。

      2. 是的,您可以将任意代码放入宏中。但是您通常应该将多语句宏包装到 do { ::: } while (0) 构造中,因为它是唯一包含终止 ; 的构造。所以你改变了你的宏:

        #define MACRO_NAME(object, expression) do { \
        /*as before*/ \
        } while (0)
        

      如果没有这个,在宏后面加上分号将不属于您所期望的位置。考虑一下:

      if (someCondition)
        MACRO_NAME(a, b);
      else
        doStuff();
      

      您会收到语法错误,因为宏之后的 ; 会终止整个 if,而导致 else 不匹配。


      附加说明:您应该检查ClassPtr1取消引用它以获得ClassPtr2

      【讨论】:

        【解决方案5】:

        1) 是的,如果你修复括号并且在检查它是否为 NULL 之前不要使用可以为 NULL 的指针,你可以这样做。

        2) 是的,预处理器给出了 OK 代码。

        使用代码(您的代码进行了一些修复以使其编译):

        #include <exception>
        #include <string>
        #define MACRO_NAME(object,expression){\
            Class1* ClassPtr1 = dynamic_cast<Class1*>(object);\
            Class2* ClassPtr2 = ClassPtr1->SomeMethod();\
            if ((ClassPtr1) && (ClassPtr2))\
            {\
                    try\
                    {\
                                return expression;\
                            }\
                    catch(const std::exception& msg)\
                    {\
                                ClassPtr2->SomeOtherMethod(msg.what());\
                                return 3;\
                            }\
            }\
            else\
                return expression;\
        }
        
        class Class2{public: int SomeOtherMethod(std::string a){return 0;}};
        class Class1{public: Class2* SomeMethod(){return 0;}};
        int main()
        {
            Class1* a;
            MACRO_NAME(a,2);
        }
        

        跑步

        cpp macro.cpp | tail -7
        

        会收获

        class Class2{public: int SomeOtherMethod(std::string a){return 0;}};
        class Class1{public: Class2* SomeMethod(){return 0;}};
        int main()
        {
         Class1* a;
         { Class1* ClassPtr1 = dynamic_cast<Class1*>(a); Class2* ClassPtr2 = ClassPtr1->SomeMethod(); if ((ClassPtr1) && (ClassPtr2)) { try { return 2; } catch(const std::exception& msg) { ClassPtr2->SomeOtherMethod(msg.what()); return 3; } } else return 2;};
        }
        

        【讨论】:

          【解决方案6】:

          具有控制结构(如循环或 if/else)的多行宏是可能的。最佳实践(在避免 ;-) 之后)是将所有内容包装在“do{ ...} while(false)”中,如下所示:

          #define MACRO_NAME(object,expression)\
          do { if(...) { doSomething; } else { doSomethingElse; } } while(false)
          

          do...while 循环在语法上与单个语句兼容,这与普通块或 if...else 不同。例如,它与用户将在其后面写的分号配合得很好。你可以在 if.. else 中使用它:

          if(cond) MACRO(); else exit();
          

          不会按照您的建议进行编译,因为 MACRO 后面的分号会创建第二个空语句,因此“else”是悬空的。 do...while.

          【讨论】:

            【解决方案7】:

            其实我有两个问题。 这是“ if (ClassPtr1) && (ClassPtr2) ”检查它们是否不为空的正确方法吗?

            是的,检查是正确的。不过这个宏有一些问题(见下文)。

            I want to know if it is possible to include an if else statement completely inside a multi-line macro. I have attached example code.
            

            是的。以下是您的宏的一些问题(以及为什么不应该使用宏来代替函数):

            • 您在检查第一个指针是否为空之前取消引用它;
            • 宏看起来像一个函数,但不能这样使用。

            考虑这个客户端代码:

            if(some_condition)
                MACRO_NAME(MyObject, a+b);
            

            计算结果为:

            if(some_condition)
                Class1* ClassPtr1 = dynamic_cast<Class1*>(object);
            Class2* ClassPtr2 = ClassPtr1->SomeMethod();
            // ...
            

            这很可能不是您想要的(因为 ClassPtr2 声明将发出编译错误 - ClassPtr1 是在 if 块中本地定义的)。

            解决方案:

            1. do {} while(false) 将宏内的代码括起来。这将确保宏扩展为单个代码块。宏可以工作,但仍然会产生使用宏编写代码所固有的所有问题。

            2. 将宏替换为引发异常的模板函数(这是正确的解决方案)。作为(非通用)经验法则,使用宏编写重复代码是 API 设计不完整的症状(就像使用 dynamic_cast 是不良类层次结构设计的症状一样)。

            【讨论】:

              猜你喜欢
              • 2018-09-20
              • 1970-01-01
              • 1970-01-01
              • 2021-06-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-04-21
              • 1970-01-01
              相关资源
              最近更新 更多