coin cheung
coin cheung

Reputation: 1095

How could I use istream_iterator to convert unsigned char vector to a string?

I can do it with plain for loop:

vector<unsigned char> v{3, 13, 23, 83};
stringstream ss;
for (auto &el : v)
    ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(el);
cout << ss.str() << endl;

I am seeking methods that allow me to use std::copy:

std::copy(
    std::begin(v),
    std::end(v),
    std::ostream_iterator<int>(ss << std::hex << std::setw(2) << std::setfill('0'))
);

which does not give me same output values as the for loop method. I have two questions on this:

  1. how could I make it work so that the std::copy method gives identical output values and format as the for-loop method?
  2. which of the two methods is better in theory ?

Upvotes: 0

Views: 247

Answers (2)

jignatius
jignatius

Reputation: 6474

In order to use the standard algorithms and an output iterator we can define a new class and overload the stream insertion operator <<. The operator << is called by std::copy for every item we want to stream.

template <class T>
class HexOut
{
public:
    HexOut(T val) : value(val)
    {
    }

    friend std::ostream& operator<<(std::ostream& os, const HexOut<T>& elem)
    {
        os << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(elem.value) << std::dec;
        return os;
    }

private:
    T value;
};

int main()
{
    std::vector<unsigned char> v{ 3, 13, 23, 83 };
    std::copy(v.begin(), v.end(), std::ostream_iterator<HexOut<unsigned char>>(std::cout));
    return 0;
}

Here the operator << is setting the right output flags each time and casting the unsigned char to an int. As you can see we had to write more code and define a new class. This is probably overkill for a simple task as this. However, using an iterator makes it portable across different types of streams and we take advantage of the many algorithm functions such std:copy. Plus all the formatting is contained inside the operator <<, which makes it more maintainable.

Upvotes: 1

Isaac Clancy
Isaac Clancy

Reputation: 418

Your method using std::copy is the same as

ss << std::hex << std::setw(2) << std::setfill('0');
std::copy(
    std::begin(v),
    std::end(v),
    std::ostream_iterator<int>(ss)
);

the width set by std::setw goes away after the first output operation so only the output for the first character will have it.

To use std::copy, you would have to write a custom iterator instead of using std::ostream_iterator.

Your first method is better because it is shorter and easier to understand.

Upvotes: 1

Related Questions