Search code examples
c++c++17uuidiostreamiomanip

How do I get my Hex String to include intermediate zeroes in C++?


I am trying to output a UUID stored as a vector of bytes as a Hex String with dashes. When any individual byte has a leading zero, my output string skips it. I am using std::setw(2) and am still getting this behavior.

I tried the following code:

#include <iomanip>
#include <iostream>

std::ostream& operator<<(std::ostream &os, const Id &id) {
  std::ios oldState(nullptr);
  oldState.copyfmt(os);
  os << std::hex << std::uppercase << std::setfill('0') << std::setw(2);
  for (int i = 0; i < 16; i++) {
    os << (int)id.id_[i];
    if (i == 3 || i == 5 || i == 7 || i == 9) os << '-';
  }
  os.copyfmt(oldState);
  return os;
}

My expected output would be something like:

01020304-0102-0304-0506-AA0F0E0D0C0B

and instead I get:

1234-12-34-56-AAFEDCB

I'm obviously doing something wrong here. I've tried flushing the buffer and inserting the dashes after the fact and still no luck.


Solution

  • As was explained in the comments, std::setw is a one-time manipulator. It works on the next output item only. After that, the stream width is reset to 0.

    The fix is easy enough: Move setw into the loop.

    For demonstration purposes, I created struct Id.

    // main.cpp
    #include <cstdint>
    #include <iomanip>
    #include <iostream>
    #include <vector>
    #include <initializer_list>
    
    struct Id
    {
        Id(std::initializer_list<std::uint8_t> l)
            : id_{ l }
        {}
        std::vector<std::uint8_t> id_;
    };
    
    std::ostream& operator<<(std::ostream& os, const Id& id) {
        std::ios oldState(nullptr);
        oldState.copyfmt(os);
        os << std::hex << std::uppercase << std::setfill('0');
        for (int i = 0; i < 16; i++) {
            os << std::setw(2) << (int)id.id_[i];  // <===== setw goes here
            if (i == 3 || i == 5 || i == 7 || i == 9) os << '-';
        }
        os.copyfmt(oldState);
        return os;
    }
    int main()
    {
        Id id{
            0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 
            0x05, 0x06, 0xAA, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B
        };
        std::cout << id << '\n';
        return 0;
    }
    

    Output:

    01020304-0102-0304-0506-AA0F0E0D0C0B