is there a good way to combine stream manipulators?
Asked Answered
P

3

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?

Parasiticide answered 1/7, 2010 at 15:45 Comment(1)
boost has an output format library: see #119598Ogletree
G
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;
}
Gooseflesh answered 1/7, 2010 at 15:59 Comment(7)
+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<N>.Johns
@Jon good call! I have edited the post accordingly to include that as a possibility.Gooseflesh
I believe what you've typed in your first example doesn't work. You need to write hex4(cout) << 123 << endl;.Perceval
@Perceval 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<<.Gooseflesh
@Perceval 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.Gooseflesh
Wow, I learned something new! Thanks! I think you should add that explanation to your answer :)Perceval
@Perceval you're welcome! :-) I added it to the answer; thanks for the suggestion!Gooseflesh
O
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;
}
Ossuary answered 1/7, 2010 at 15:51 Comment(0)
L
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.

Leanto answered 5/3, 2021 at 0:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.