Tavy
Tavy

Reputation: 871

date format using preprocessor defines

I want to print a date with '0' before numbers lower than 10 using preprocessor code. For example, if i have the numbers 11,1,2015, the output should be 11 01 2015

I have this preprocessor:

 #define getDateFromVar(n) \
 ( (var##n<10) ? ('0' << var##n) : (var##n ) )

And my C++ code is this:

int var1 = 11, var2 = 1, var3 = 2015;
cout << "The date is: " << getDateFromVar(1) 
                 << " " << getDateFromVar(2)
                 << " " << getDateFromVar(3) << endl;

The putput is: 11 96 2015 . 96 is equal with 48 left shifted by 1. In this sequence ('0' << var##n) , << are not considered cout streams.

Upvotes: 0

Views: 138

Answers (2)

Ben
Ben

Reputation: 380

This happens because the ?: ternary operator is being handled by the compiler, not by the preprocessor.

When type the following code,

int var2 = 1;
cout << getDateFromVar(2) << endl;

what it appears you think will happen is this:

cout << '0' << var2 << endl;

If things worked this way, you'd end up with your output correctly padded as you'd hoped. However, what the preprocessor actually does is first substitute the macro arguments and then basically insert the entire body of the macro into the source file. This results in something like this:

cout << ( (var2<10) ? ('0' << var2) : (var2 ) ) << endl;

At this point, the preprocessor's job is done and the C++ compiler takes over. Everything inside the parenthesis is evaluated first, and if the first option of the conditional is chosen, the two arguments to the << operator are integral types ('0' and var2), so it performs it's traditional function of a left shift, shifting the ASCII value of '0' (48) one place to the left, resulting in the 96 you are observing.

I don't really think that there's a good way to achieve this using the processor. You might think that getting rid of some or all of the parenthesis may help out, but this will cause you to run into operator precedence issues, since the << operator gets higher precedence than the < operator.

If you're looking for a more conventional way to use std::cout to get output padded with zeros, I'd suggest checking out this post; if you were just looking to learn something about the preprocessor, I hope I've helped!

Upvotes: 0

nnn
nnn

Reputation: 4220

This happens because ('0' << var##n) is evaluated first, being surrounded by parentheses, and only its result is passed to cout.

You could circumvent that by changing to something like:

#define getDateFromVar(n) \
    ((var##n<10) ? "0" : "") << (var##n)
// "0" if var < 10, otherwise empty string, followed by var

But as a general rule, using macros is not recommended. Depending on scenario, it could make the code harder to read / understand, and could lead to hard to spot bugs.

As an alternative, the code could be changed to:

#include <iomanip>

cout << "The date is: " << setfill('0') 
             << setw(2) << var1 << " "
             << setw(2) << var2 << " " 
             << setw(4) << var3 << endl;

Upvotes: 1

Related Questions