7

If I wanted to output a fixed width hex number with 4 digits on a stream, I would need to do something like this:

cout << "0x" << hex << setw(4) << setfill('0') << 0xABC;

which seems a bit long winded. Using a macro helps:

#define HEX(n) "0x" << hex << setw(n) << setfill('0')

cout << HEX(4) << 0xABC;

Is there a better way to combine the manipulators?

stinky472
  • 6,737
  • 28
  • 27
markh44
  • 5,804
  • 5
  • 28
  • 33
  • boost has an output format library: see http://stackoverflow.com/questions/119098/which-i-o-library-do-you-use-in-your-c-code/119194#119194 – Martin York Jul 01 '10 at 16:14

3 Answers3

19

Avoid the macros when you can! They hide code, making things hard to debug, don't respect scope, etc.

You can use a simple function as KenE provided. If you want to get all fancy and flexible, then you can write your own manipulator:

#include <iostream>
#include <iomanip>
using namespace std;

ostream& hex4(ostream& out)
{
    return out << "0x" << hex << setw(4) << setfill('0');
}

int main()
{
    cout << hex4 << 123 << endl;
}

This makes it a little more general. The reason the function above can be used is because operator<< is already overloaded like this: ostream& operator<<(ostream&, ostream& (*funtion_ptr)(ostream&)). endl and some other manipulators are also implemented like this.

If you want to allow the number of digits to be specified at runtime, we can use a class:

#include <iostream>
#include <iomanip>
using namespace std;

struct formatted_hex
{
    unsigned int n;
    explicit formatted_hex(unsigned int in): n(in) {}
};

ostream& operator<<(ostream& out, const formatted_hex& fh)
{
    return out << "0x" << hex << setw(fh.n) << setfill('0');
}

int main()
{
    cout << formatted_hex(4) << 123 << endl;
}

If the size can be determined at compile-time, however, might as well just use a function template [thanks to Jon Purdy for this suggestion]:

template <unsigned int N>
ostream& formatted_hex(ostream& out)
{
    return out << "0x" << hex << setw(N) << setfill('0');
}

int main()
{
    cout << formatted_hex<4> << 123 << endl;
}
stinky472
  • 6,737
  • 28
  • 27
  • 3
    +1 I would prefer a manipulator to a free function, since it seems to better convey the intent. To make it fully generic, write it as a template `hex`. – Jon Purdy Jul 01 '10 at 16:04
  • @Jon good call! I have edited the post accordingly to include that as a possibility. – stinky472 Jul 01 '10 at 16:08
  • I believe what you've typed in your first example doesn't work. You need to write `hex4(cout) << 123 << endl;`. – rlbond Jul 02 '10 at 05:59
  • @rlbond The code works but free to try it out if in doubt. This is manipulator syntax (endl itself is a function which takes an ostream argument by reference). The reason it works is because operator<< is already overloaded like this: `ostream& operator<<(ostream&, ostream& (*funtion_ptr)(ostream&))`. Thus we can use the address of any function which accepts ostream by reference and returns ostream by reference with the insertion `operator<<`. – stinky472 Jul 02 '10 at 06:16
  • @rlbond that's also why the third example works (we're passing a function address to a function pointer argument as the right operand for operator<<). The second example, using a class/struct, doesn't have an operator<< already overloaded for us, however, and that's why we have to define an overloaded operator<< for that one. – stinky472 Jul 02 '10 at 06:24
  • Wow, I learned something new! Thanks! I think you should add that explanation to your answer :) – rlbond Jul 02 '10 at 17:53
  • @rlbond you're welcome! :-) I added it to the answer; thanks for the suggestion! – stinky472 Jul 03 '10 at 03:36
4

Why a macro - can't you use a function instead?

void write4dhex(ostream& strm, int n)
{
    strm << "0x" << hex << setw(4) << setfill('0') << n;
}
KenE
  • 1,805
  • 10
  • 6
1

In C++20 you'll be able to use std::format to make this much less verbose:

std::cout << std::format("0x{:04x}", 0xABC);  

Output:

0x0abc

You can also easily reuse the format string by storing it in a constant.

In the meantime you can use the {fmt} library, std::format is based on. {fmt} also provides the print function that makes this even easier and more efficient (godbolt):

fmt::print("0x{:04x}", 0xABC); 

Disclaimer: I'm the author of {fmt} and C++20 std::format.

vitaut
  • 49,672
  • 25
  • 199
  • 336