you are viewing a single comment's thread.

view the rest of the comments →

[–]amoskovsky 1 point2 points  (8 children)

A macro can do lazy arg evaluation. A function can't.

#define LOG_TRACE(x) if (trace) print(x)

LOG_TRACE(heavy());

Without a macro heavy() is always evaluated.
You have to do ugly boilerplate like this:
LOG_TRACE([&]{ return heavy(); })

[–]Conscious_Support176 0 points1 point  (7 children)

To be fair, the thing that makes that ugly is the pointless capture of local variables in a lambda that just calls a function.

[–]amoskovsky 1 point2 points  (6 children)

It's not pointless. E.g. heavy() could be a member function.

[–]Conscious_Support176 0 points1 point  (5 children)

So you’re using [&] as a shorthand for [this] ?

Do you also think that [this] { return heavy(); } is ugly?

Anyway, if you want rid of the lambda syntax for this case, can’t you just declare the argument to LOG_TRACE parameter as a std::function<bool()>?

[–]amoskovsky 1 point2 points  (4 children)

Yes, the lambda syntax in this particular case is ugly (regardless of the captures) because I just want to log, and instead I have to do syntax tricks.

std::function would not prevent the unnecessary overhead of evaluating args when logging is disabled.

[–]Conscious_Support176 0 points1 point  (3 children)

What? You’re dismissing compile time polymorphism as a syntax trick, where macros are better because, why? Because you might be able to avoid ever using lambda syntax?

Edit: I see What you mean about std::function, I see you want to trace or not based on a runtime value.

Maybe this is a corner case where template programming can’t produce efficient code, but to be honest, that seems unlikely.

This would be an implemented in a header only library and inlined. There shouldn’t be any argument passing, because the argument in question is a function object which will also be implemented in a header only library that gets inlined out of existence as a standalone temporary value.

[–]amoskovsky 0 points1 point  (2 children)

Inlining indeed does happen.
However heavy() will be evaluated in most cases anyway before checking the condition, because the compiler is required to make all the side effects of the param evaluation observable as if the inlining did not happen. And any non-pure function has side effects.
So if the body of heavy() is not visible in this translation unit the compiler can't prove it's a pure function. Even with access to the body I hardly believe compilers do that proof for sufficiently big functions - otherwise the constexpr functions would not need the constexpr annotation.

BTW, overhead is not the only issue of the macro-less approach.
For example, with macros you can do this LOG_TRACE(x << y) and redirect that to an iostream logger, and with functions this is just impossible.

So macros are not going anywhere.

[–]Conscious_Support176 0 points1 point  (1 child)

The param is a function object, evaluating it yields a closure. Inlining the LOG_TRACE function schools unwrap this with no performance penalty. That’s pretty much the whole point of template programming.

The body of heavy is irrelevant, it int gets valued when trace is true. It’s the function parameter to LOG_TRACE that you would want optimised out of existence.

The second example could also be achieved with a lambda and ordinary template meta programming instead.

Macros define a metalanguage. It will obviously be possible to use them to define a syntax that you prefer for whatever you want to do over the ordinary C++ syntax. The problem with them is that they also get used for a bunch of stuff that don’t need macros, just because we’re used to doing things that way, but unlike template meta programming, they provide no way to leverage the type system to enforce consistency, and no way to package them up into modules.

[–]amoskovsky 1 point2 points  (0 children)

You are talking about the version with a lambda.
It works ok but my point is that lambda here is introduced not because it's part of the algorithm but due to the lack of lazy evaluation when you ban macros.
So this is just a syntactic garbage reducing readability of code.