Reputation: 23
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';
}
Upvotes: 1
Views: 492
Reputation: 6481
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
Upvotes: 2
Reputation: 238391
How to use Macros in c++?
Sparingly. When there is no better alternative.
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?
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.
Practically identical to 2. The auto
parameter turns these into function templates which are implicitly inline.
Pro: Doesn't require C++20. A reference is passed, which is good for passing strings.
In conclusion: 4. is a reasonable default choice.
Upvotes: 4
Reputation: 46
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.
Upvotes: 0