简体   繁体   中英

If-else statement inside multi-line macro (C++)

I have two questions actually.

  1. Is this " if (ClassPtr1) && (ClassPtr2) " the right way to check if they are not null?

  2. 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.

     #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;\\ } 

1: No. You're using ClassPtr1 before the check, which is undefined behavior if it actually is null. You need to first get ClassPtr1 , then check it, then use it to obtain ClassPtr2 , and then check that.

2: Yes, that's fine in principle. Whether the macro is a good idea at all is a different issue.

1: You can check with the c++11 keyword nullptr.

2: Yes you can!

Yes, it works. The only thing is that you're missing the if mandatory parentheses, so it will not compile as is once expanded.

Also, you must check both pointers before accessing them, or you will get undefined behavior, as pointed out by @Sebastian Redl.

  1. It's not quite correct syntax; that would be if (ClassPtr1 && ClassPtr2) (the condition of an if must be enclosed in parentheses).

  2. Yes, you can put arbitrary code into a macro. But you should generally wrap multi-statement macros into a do { ::: } while (0) construct, as it's the only one that includes a terminating ; . So you'd changed your macro like this:

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

Without this, following the macro with a semi-colon would not belong where you expect. Consider this:

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

You'd get a syntax error, because the ; after the macro terminates the entire if , leaving the else unmatched.


Additional note: you should check ClassPtr1 before you dereference it to obtain ClassPtr2 .

1) Yes, you can do that if you fix your parentheses and don't use a pointer that can be NULL before checking if it is NULL.

2) Yes, the preprocessor gives OK code.

With the code (yours with a few fixes to make it compile):

#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);
}

Running

cpp macro.cpp | tail -7

Will yield

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;};
}

Multi line macros with control structures like loops or if/else are possible. Best practice (after avoiding ;-) ) for that is to wrap everything in a "do{ ...} while(false)" like this:

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

A do...while loop is syntactically compatible with a single statement, as opposed to a normal block or an if...else. It plays nicely with the semicolon a user will write behind it, for example. You can use it in if.. else:

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

Wouldn't compile with your suggestion bacause the semicolon behind the MACRO would create a second, empty statement so that the "else" is dangling. Not so with the do... while.

I have two questions actually. Is this " if (ClassPtr1) && (ClassPtr2) " the right way to check if they are not null?

Yes, the check is correct. The macro has a few problems though (see below).

 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. 

Yes. Here are some problems with your macro (and why you shouldn't use macros in place of functions):

  • You are dereferencing the first pointer before checking if it's null;
  • The macro looks like a function, but it will not be usable as such.

Consider this client code:

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

This evaluates to:

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

This is most probably not what you want (as the ClassPtr2 declaration will issue a compilation error - ClassPtr1 is locally defined within the if block).

Solutions:

  1. surround the code within the macro with do { and } while(false) . This will ensure that the macro expands to a single block of code. The macro will work, but still create all problems inherent in using macros to write code.

  2. Replace the macro with a template function that throws an exception (this is the correct solution). As a (non-universal) rule of thumb, writing repetitive code with a macro is a symptom of incomplete API design (just like the use of dynamic_cast is a symptom of bad class hierarchy design).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM