Printing zero-padded hex with std::cout [duplicate]
Asked Answered
T

3

17

Say I have a dword I want to output in hex with std::cout and left-pad with zeros, so 0xabcd will be shown as 0x0000abcd. It seems like you would have to do this:

uint32_t my_int = 0xabcd;

std::cout << "0x" << std::hex << std::setw(8) << std::setfill('0')
    << my_int << std::endl;

This seems ridiculous for something that can be accomplished in C with printf("0x%08X\n", my_int);. Is there any way to make this shorter while still using std::cout for output (besides using namespace std)?

Teresitateressa answered 28/1, 2014 at 20:43 Comment(3)
Write a function to output as formatted hex and call it.Ninnetta
Shorter is a relative thing, I suppose. If you think the code behind that printf() is short I can assure you it is anything but. You'll just need to roll your own (which will look shocking similar to what you have here). You could always write a manipulator class/operator pair that allows something like std::cout << as_hex(n,8,0);Metrics
To literally make it shorter, I'd use a macro to hide the ugliness, e.g. #define PAD_HEX(digits) std::hex << std::setw(digits) << std::setfill('0') then later in the code use it as std::cout << "0x" << PAD_HEX(8) << my_int ..Herwig
P
17

I suppose you can write a "stream manipulator". This is useful if you have multiple hex numbers you want to print in this format. This is clearly not an ideal solution, but using a wrapper type you can make your own "format flag" to toggle it. See Sticky custom stream manipulator for more information.

#include <iostream>
#include <iomanip>

static int const index = std::ios_base::xalloc();

std::ostream& hexify(std::ostream& stream) {
    stream.iword(index) = 1;
    return stream;
}

std::ostream& nohexify(std::ostream& stream) {
    stream.iword(index) = 0;
    return stream;
}

struct WrapperType {
    uint32_t _m;
public:
    WrapperType(uint32_t m) : _m(m)
    {
    }

    uint32_t getm() const
    {
        return _m;
    }
};
std::ostream& operator<< (std::ostream& os, const WrapperType& t) {
    if (os.iword(index))
        return os << "0x" << std::hex << std::setw(8) << std::setfill('0') << t.getm();
    else
        return os << t.getm();
}

int main()
{
    WrapperType my_int{0xabcd};
    std::cout << hexify << my_int << my_int;
    std::cout << nohexify << my_int;
}
Phonic answered 28/1, 2014 at 20:51 Comment(2)
+1 totally the way I would do this, but the width manipulator is reset (as I recall) on each issuance to the stream operator <<, so you may have to make it part of a paramaterized manipulator. I may be wrong on that, but it seems like that was the way they worked (or it was the opposite and I'm having a dyslexic moment). Edit: just tested, you're totally right. I was dyslexic. sry for the confusion.Metrics
@Metrics I edited my answer, but I'm not sure if it's better. I mean it may be annoying to have to remember use WrapperType for formatting purposes, which is where your solution to just use a function is better. But if OP is lazy, maybe this would save typing.Phonic
L
5

I would not change the (global) flags of a stream, just a manipulator:

#include <iostream>
#include <iomanip>
#include <limits>

template <typename T>
struct Hex
{
    // C++11:
    // static constexpr int Width = (std::numeric_limits<T>::digits + 1) / 4;
    // Otherwise:
    enum { Width = (std::numeric_limits<T>::digits + 1) / 4 };
    const T& value;
    const int width;

    Hex(const T& value, int width = Width)
    : value(value), width(width)
    {}

    void write(std::ostream& stream) const {
        if(std::numeric_limits<T>::radix != 2) stream << value;
        else {
            std::ios_base::fmtflags flags = stream.setf(
                std::ios_base::hex, std::ios_base::basefield);
            char fill = stream.fill('0');
            stream << "0x" << std::setw(width) << value;
            stream.fill(fill);
            stream.setf(flags, std::ios_base::basefield);
        }
    }
};

template <typename T>
inline Hex<T> hex(const T& value, int width = Hex<T>::Width) {
    return Hex<T>(value, width);
}

template <typename T>
inline std::ostream& operator << (std::ostream& stream, const Hex<T>& value) {
    value.write(stream);
    return stream;
}

int main() {
    std::uint8_t u8 = 1;
    std::uint16_t u16 = 1;
    std::uint32_t u32 = 1;
    std::cout << hex(unsigned(u8), 2) << ", " << hex(u16) << ", " << hex(u32) << '\n';
}
Levan answered 28/1, 2014 at 21:38 Comment(1)
I prefer this approach. I'm not sure how the math compares, but I also prefer the default width to be calculated as: (sizeof(T) * 2) (2 hex characters per byte). That seems simpler to me.Starveling
W
1

My C++ is rusty, but how about using Boost formatting: http://www.boost.org/doc/libs/1_37_0/libs/format/index.html

Weikert answered 28/1, 2014 at 20:47 Comment(1)
This would have suite better as comment.Chavey

© 2022 - 2024 — McMap. All rights reserved.