0

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.

Tavy
  • 861
  • 11
  • 15
  • 1
    Why do you want to do that? What is the point of trying to do it with a macro? – Alan Stokes Dec 04 '15 at 23:53
  • I just want to learn more about preprocessor code and i am curious if it is possible to print something in this way – Tavy Dec 04 '15 at 23:58
  • Try to get rid of parentheses. Parentheses has the highest precedence out of all operators, so anything inside parentheses is done first, i.e. before the code actually realizes that you are using stream, and, that is, expected behavior (since, without it, there would be no way of `cout`ing expressions directly. – Algirdas Preidžius Dec 05 '15 at 00:18

2 Answers2

1

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;
nnn
  • 3,980
  • 1
  • 13
  • 17
  • 1
    I'd be tempted to add `setw(4)` or `setw(4) << setfill('0'` to the third line for symmetry, though when the numbers are fixed as shown, maybe it doesn't matter. – Jonathan Leffler Dec 05 '15 at 00:45
0

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!

Community
  • 1
  • 1
Ben
  • 370
  • 1
  • 2
  • 6
  • Your big bold headline is brave but wrong. Proof by counter-example — file `ppt.c` containing: `#define M0 39 / #define M1 0 / #define M2 11 / #define M3 22 / #ifndef Mx / #define Mx M0 / #endif / #if ((Mx > M3) ? M1 : M2) > 5 / this is overtaken / #else / that is undertaken / #endif` where the slashes indicate line breaks. Compilation: `gcc -E -DMx=19 -std=c89 -pedantic ppt.c` generates output containing `this is overtaken` but using `-DMx=44` instead it generates output containing `that is undertaken`. I seriously doubt if this is an MCVE, but it did prove my point — at least with GCC. – Jonathan Leffler Dec 05 '15 at 01:01
  • Ha, that's pretty cool actually - didn't know you could do that. I'll edit to clarify that in this case, the `?:` is being handled by the compiler, not the preprocessor, but to remove the assertion that `?:` has no meaning to the preprocessor at all. I still don't see how this could work with macro arguments though, unless you're also going to tell me that I'm also mistaken in my belief that you can't have preprocessor `#if` / `#else` / ` #endif` statements in a macro body :P. – Ben Dec 05 '15 at 02:11
  • Please run the code and the preprocessor and look at the output. There are no `?:` operators in the output from the preprocessor, so the preprocessor is fully evaluating the `?:` operator. – Jonathan Leffler Dec 05 '15 at 04:42
  • I did, and I agree that in your example, the `?:` is handled by the preprocessor - there is no `?:` in the output. However, I'm asserting that in the case of the original question, it isn't. If you put the asker's code in a file, and run GCC against it using your same command line, the `?:` does appear in the output from the preprocessor. – Ben Dec 05 '15 at 05:46
  • Sorry; I misunderstood what you are saying. Yes, in the macro in the question the `?:` operator is in the body of the macro and is not in a condition evaluated by the preprocessor. – Jonathan Leffler Dec 05 '15 at 05:48