简体   繁体   中英

How to use Macros in c++?

Recently I came across inline in c++17 and how it been used as an alternative to #define but both have its downsides. However my question is that if I want to cramp the whole std::cout << x << '\n'; in one simple line called LOG(x) .

A: What should I use and why?
B: What are the pros and cons of each and when to use what?

1.)

#define LOG(x) std::cout << x << '\n'

2.)

void inline LOG(auto x)
{
    std::cout << x << '\n';
}

3.)

void LOG(auto x)
{
    std::cout << x << '\n';
}

4.)

This one was suggested to me by someone:

template <typename T>
void log(const T& x)
{
    std::cout << x << '\n';
}

How to use Macros in c++?

Sparingly. When there is no better alternative.


  1. Avoid this. Macros names don't respect scoping rules and thus are much more susceptible to name clashes. Also, consider what happens if you wanted to log a number bitshifted by another number:

     LOG(0x1 << 2);

    What output would you expect, and what output do you get? Do they match?

  2. Pro: It is a function (template) and thus doesn't have the problems associated with macros. Con: You accept the parameter by value. This can be expensive with for example strings which are quite often used for logging. Requires C++20.

  3. Practically identical to 2. The auto parameter turns these into function templates which are implicitly inline.

  4. Pro: Doesn't require C++20. A reference is passed, which is good for passing strings.

In conclusion: 4. is a reasonable default choice.

The general consensus is that using macros is not a good idea. BUT what that really means is that ABUSE of macros is not a good idea.

Solution #2 is OK, but its usefulness is rather limited, since you can only print one value per line.

inline void LOG(const auto& x)
{
    std::cout << x << '\n';
}

// use as:
LOG(x);

And it's an OK solution, but its usage is rather limited.. Consider this use case:

inline void LOG(const auto& x)
{
    std::cout << x << '\n';
}

struct point { float x, y; };

// usability is kind of limited, since it will only print one value per line 
point p{};
LOG(x);
LOG(y);

// which gives this output.
0
0

// That's not really useful for a log.

One advantage of the macro, is that you could make your log output a bit more useful.

#define LOG(x) std::cout << x << '\n'

struct point { float x, y; };

// usability is better, but still limited, 
point p{};
LOG("x: " << p.x << ", y: " << p.y);

// which gives this output.
x: 0, y: 0

That's a bit better, you are using a macro, and you do have more control over the output, making your log more useful. But it puts some limitations on your code... For example, you may want, at a later date, to use a third party logging library, or write your own, but some calls to LOG() will have operators in them, and this may force you to rewrite them.

For this reason, among others, a function template would be better, one that accepts any number of arguments.

template <typename... Args>
inline void LOG(Args&&... args)
{
    (std::cout << ... << args) << '\n';
}

point p{};

LOG("x: ", p.x, ", y: ", p.y);

// which gives this output.
x: 0, y: 0

If you want to defeat logging in your release version, I suggest using a macro.

#define LOG(...)

// on MSVC
#define LOG(...) __noop

I wouldn't use #1 (macro version) unless you want to log the symbol name of the variable in the output, such as:

#define LOG(x) std::cout << #x << " = " << x << '\n' 

I believe #2 and #4 are equivalent, as using auto introduces a template type under the hood.

#3 is slightly inferior as it requires you to write a declaration in a header file as well as putting the definition in a source file (since it is not inline), which is more code to maintain.

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