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.
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.
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.
Related
HI ,
Can some one help me in understanding why the value of SQUARE(x) is 49 ?
I am using Visual C++ 6.0 .
#define SQUARE(X) X * X
int main(int argc, char* argv[])
{
int y = 5;
printf("%d\n",SQUARE(++y));
return 0;
}
Neil Butterworth, Mark and Pavel are right.
SQUARE(++y) expands to ++y * ++y, which increments twice the value of y.
Another problem you could encounter: SQUARE(a + b) expands to a + b * a + b which is not (a+b)*(a+b) but a + (b * a) + b. You should take care of adding parentheses around elements when needed while defining macros: #define SQUARE(X) ((X) * (X)) is a bit less risky. (Ian Kemp wrote it first in his comment)
You could instead use an inline template function (no less efficient at runtime) like this one:
template <class T>
inline T square(T value)
{
return value*value;
}
You can check it works:
int i = 2;
std::cout << square(++i) << " should be 9" << std::endl;
std::cout << square(++i) << " should be 16" << std::endl;
(no need to write
square<int>(++i)
because the int type is implicit for i)
Because the macro expands to:
++y * ++y
which gives undefined behaviour in C++ - the result could
be anything. This very well known problem should be covered in any decent textbook that covers the use of macros. Which one are you using?
Because macros do textual substitution so the code you wrote gets expanded to
printf("%d\n",++y * ++y );
and then the order of operations is undefined behaviour so this the compiler sees 2 increments and then a multiplication
So be careful with macros better to use functions which as the compiler can expand inline will not take any longer to run.
Secondly don't assume what will happen if you increment and use variables
Macros are not functions: they just alter the text of the program. This operation is called preprocessing and it's automatically executed before your code gets compiled. People write macros to save their time and introduce some variability to their source code.
When you write SQUARE(x), no actual funciton call happens, just the text is modified. The operation is quite dumb, so you have to do additional precautions in cases like yours. Refer to other answers for explanation of your case.
I'm trying to convert a working define which prints the argument into a proper function, but I'm unsure of the variable type.
The working method I have is:
#define WARN(x) std::cout << "WARNING! " << x << "!" << std::endl;
Which I can then pass a sentence similar to using std::cout
WARN("This is a test warning." << " And this is some more stuff...");
However, I feel this should be more neatly wrapped into a function rather than using a define.
Please could you help me understand how the preprocessor is interpreting the variable x, and how I can translate the define into a function.
P.S. I am aware that I could use a variadic function, but I'd rather not have to pass the number of arguments and would rather avoid variadic altogether anyway.
but I'm unsure of the variable type.
That's where use of function templates can provide a clean solution.
template <typename T>
void WARN(T const& x)
{
std::cout << "WARNING! " << x << "!" << std::endl;
}
is a good replacement for
#define WARN(x) std::cout << "WARNING! " << x << "!" << std::endl;
Please RTFM me if needed, as so far I may have been searching for the wrong things!
On the Arduino
Serial << "sdf" << var;
works a treat (with streaming.h referenced), but
#if (CRIPWEB_DEBUG_BITS & DEBUG_CMD)
Serial << F("Run Macro: ") << strMacro << "\n";
#endif
takes three lines and makes the code unreadable.
I'd like to say something along the lines of:
Debug(CRIPWEB_DEBUG_BITS & DEBUG_CMD) << F("Run Macro: ") << strMacro << "\n";
Is this possible? I'd be happier with:
Debug.set(CRIPWEB_DEBUG_BITS & DEBUG_CMD); Debug << F("Run Macro: ") << strMacro << "\n";
I'll worry about size after I've had a go at this!
Many thanks,
Glyn
The preprocessor would be compile-time, the other would not. You really want the slowdown?
Anyway, if you accept a slightly different syntax, we get pretty near to what you wanted while not giving up on eliminating the useless code at compile-time:
#define COND(cond, ...) ((void)((exp) && (__VA_ARGS__, 0)))
Use as
COND(CRIPWEB_DEBUG_BITS & DEBUG_CMD, Serial << F("Run Macro: ") << strMacro << "\n");
Or, if you only ever test that one condition:
#define IFDEBUG(...) ((void)((CRIPWEB_DEBUG_BITS & DEBUG_CMD) && (__VA_ARGS__, 0)))
Use as
IFDEBUG(Serial << F("Run Macro: ") << strMacro << "\n");
You could write your own class to do exactly that.
I am answering only compile-time solution for the expression:
Serial << "sdf" << var;
Assume Serial is a global instance of class. Let's say instance of class DebugWriter. This class has overloaded operator <<. Now, you can make another class FakeDebugWriter, which will also have operator << implemented, but that would do nothing. Further, you can declare Serial instance as:
#if CRIPWEB_DEBUG_BITS & DEBUG_CMD
DebugWriter Serial;
#else
FakeDebugWriter Serial;
#endif
But it should be noted that Serial<< will still make call to function in case of FakeDebugWriter - though compiler may omit out the function call.
Therefore, it would be better to write debug-logging as function-style macro only. You can craft the macro has do some thing (or something heavy), and another as Do-nothing.
EDIT: After comment from OP. Here is analogues similar example.
SmallInt a, b;
Where SmallInt would be type-defined, depending on value(s) of pre-processor macro (not macro macro).
#if SomeCondition
typedef int SmallInt;
#else
typedef short SmallInt;
#endif
I try to force my compiler to replace any std::cout occurrence in my code with something.
But when I write something like that:
#define std::cout (*some code*)
My compiler spit on my face. Is there a way to do this ?
EDIT :
Here is the code snippet:
# define std::cout (std_out << std::cout)
(std_out is a file I've previously open)
and the error on a line with a std::out occurence
the global scope has no "cout"
You define an identifier, not an arbitrary expression. std is
an identifier; your define will cause the compiler to replace
every instance of the identifier std with ::cout (*some
code*). So it's not surprising that the compiler doesn't like
it: std::cout << toto becomes ::cout (*some code*)::cout <<
toto and std::vector becomes ::cout (*some code*)::vector.
If you'd explain what you're actually trying to achieve, we could probably help you better.
I try to force my compiler to replace any std::cout occurence in my code with something
That's a bad idea. If you are looking for configurable behavior on your output stream, replace all occurrences of std::cout in your code with out, and declare out as std::ostream& out (= whatever stream type you may need).
My compiler spit on my face. Is there a way to do this ?
Not as such. No. You could write:
#define OUTPUT std::cout
OUTPUT << "a = " << a << std::endl;
but you needing a #define to disable (or redirect) your output stream is a sign of bad design (i.e. your define is not the problem you should be trying to solve).
You can make your own version of cout, that actually calls cout, you can place any custom code there:
std::ostream& my_cout() {
/// ...
return std::cout << "a custom message";
}
int main() {
my_cout() << " hi" << std::endl;
}
HI ,
Can some one help me in understanding why the value of SQUARE(x) is 49 ?
I am using Visual C++ 6.0 .
#define SQUARE(X) X * X
int main(int argc, char* argv[])
{
int y = 5;
printf("%d\n",SQUARE(++y));
return 0;
}
Neil Butterworth, Mark and Pavel are right.
SQUARE(++y) expands to ++y * ++y, which increments twice the value of y.
Another problem you could encounter: SQUARE(a + b) expands to a + b * a + b which is not (a+b)*(a+b) but a + (b * a) + b. You should take care of adding parentheses around elements when needed while defining macros: #define SQUARE(X) ((X) * (X)) is a bit less risky. (Ian Kemp wrote it first in his comment)
You could instead use an inline template function (no less efficient at runtime) like this one:
template <class T>
inline T square(T value)
{
return value*value;
}
You can check it works:
int i = 2;
std::cout << square(++i) << " should be 9" << std::endl;
std::cout << square(++i) << " should be 16" << std::endl;
(no need to write
square<int>(++i)
because the int type is implicit for i)
Because the macro expands to:
++y * ++y
which gives undefined behaviour in C++ - the result could
be anything. This very well known problem should be covered in any decent textbook that covers the use of macros. Which one are you using?
Because macros do textual substitution so the code you wrote gets expanded to
printf("%d\n",++y * ++y );
and then the order of operations is undefined behaviour so this the compiler sees 2 increments and then a multiplication
So be careful with macros better to use functions which as the compiler can expand inline will not take any longer to run.
Secondly don't assume what will happen if you increment and use variables
Macros are not functions: they just alter the text of the program. This operation is called preprocessing and it's automatically executed before your code gets compiled. People write macros to save their time and introduce some variability to their source code.
When you write SQUARE(x), no actual funciton call happens, just the text is modified. The operation is quite dumb, so you have to do additional precautions in cases like yours. Refer to other answers for explanation of your case.