no setw for istreams?

E

edd

Hello all,

I'm trying to put together some code that reads 3 adjacent pairs of hex
digits from an istream. Each hex digit pair represents a non-negative
integer.

For example I would like to extract 1, 255 and 18 from 01ff12 (as the
components of a simple RGB colour, in my application).

I was hoping to find something like std::setw for istreams and use it
in combination with std::hex so that I could read 2 hex digits at a
time, but as far as I can tell no such entity exists in the C++
standard library.
Is there a neat and tidy way of doing what I want, or am I going to
have to do something nasty like put each digit pair in to its own
buffer and stream-extract/strtoul the numbers from there?

Why is it that no equivalent to setw exists for istreams?

Kind Regards,

Edd
 
S

Salt_Peter

Hello all,

I'm trying to put together some code that reads 3 adjacent pairs of hex
digits from an istream. Each hex digit pair represents a non-negative
integer.

For example I would like to extract 1, 255 and 18 from 01ff12 (as the
components of a simple RGB colour, in my application).

I was hoping to find something like std::setw for istreams and use it
in combination with std::hex so that I could read 2 hex digits at a
time, but as far as I can tell no such entity exists in the C++
standard library.
Is there a neat and tidy way of doing what I want, or am I going to
have to do something nasty like put each digit pair in to its own
buffer and stream-extract/strtoul the numbers from there?

Why is it that no equivalent to setw exists for istreams?

Kind Regards,

Edd

There are a number of ways to approach the issue, and this is not
neccessarily the best way.
You could teach the computer how to count with hex char pairs.
Extracting two chars from a std::istringstream or std::istream& is
dandy but that will get you an ascii translation, not what you need.
The Hex class's const_char_array ctor takes 3 chars, not 2 (because of
null termination).
The cvt(...) function converts each hex digit to an unsigned equivalent
while the ctor(s) init lists take care of calculating the final
unsigned result. Any hex digit that falls out of range generates an
exception.
This should be a portable solution. But that remains to be seen.

#include <iostream>
#include <ostream>
#include <string>
#include <stdexcept>

class Hex
{
char m_16c; // left hex digit * 16
char m_c; // right hex digit
unsigned m_n; // result
public:
Hex(const char (& array)[3]) // ctor, char array[3] by ref
: m_16c(array[0]), m_c(array[1]),
m_n(16*cvt(m_16c) + cvt(m_c)) { }
Hex(std::string& s) // ctor, a string of 2 chars, others ignored
: m_16c(s[0]), m_c(s[1]),
m_n(16*cvt(m_16c) + cvt(m_c)) { }
Hex(const Hex& copy) // copy ctor
{
m_16c = copy.m_16c;
m_c = copy.m_c;
m_n = copy.m_n;
}
// member functions
unsigned cvt(const char c) const
{
if(c >= '0' && c <= '9')
{
return c - '0';
} else if(c >= 'a' && c <= 'f')
{
return 10 + (c - 'a');
} else if(c >= 'A' && c <= 'F')
{
return 10 + (c - 'A');
} else
{
throw std::runtime_error("Hex out of range.");
}
}
// friend operators
friend std::eek:stream&
operator<<(std::eek:stream& os, const Hex& r_h)
{
return os << r_h.m_n;
}
};

int main()
{
try {
// testing hex pairs
Hex h255("ff");
std::cout << h255 << std::endl;
Hex h16("10");
std::cout << h16 << std::endl;
Hex h0A("0A");
std::cout << h0A << std::endl;

// parsing a buffer
std::string buffer("ff100a");
std::string red(buffer, 0, 2); // substr
Hex r(red);
std::cout << "r: " << r << std::endl;
std::string green(buffer, 2, 2);
Hex g(green);
std::cout << "g: " << g << std::endl;
std::string blue(buffer, 4, 2);
Hex b(blue);
std::cout << "b: " << b << std::endl;
}
catch(std::exception& r_e)
{
std::cerr << "Error: " << r_e.what();
std::cerr << std::endl;
}
}

/*
255
16
10
r: 255
g: 16
b: 10
*/

Note how the std::string buffer in main() is substringed and loaded
into individual Hex variables.
Imagine a Colour class with 3 Hex members (r, g and b), appropriate
ctors and operator overloads and even a filereader function.
You could open a file with std::ifstream and use std::getline to load
that buffer one line at a time.
 
E

edd

Salt_Peter said:
There are a number of ways to approach the issue, and this is not
neccessarily the best way.
You could teach the computer how to count with hex char pairs.
Extracting two chars from a std::istringstream or std::istream& is
dandy but that will get you an ascii translation, not what you need.
The Hex class's const_char_array ctor takes 3 chars, not 2 (because of
null termination).
The cvt(...) function converts each hex digit to an unsigned equivalent
while the ctor(s) init lists take care of calculating the final
unsigned result. Any hex digit that falls out of range generates an
exception.
This should be a portable solution. But that remains to be seen.

[ generous code snipped ]

Many thanks for you reply. I had come up with something equally long
winded. I was hoping that istreams would provide a way of doing this
simply as the task appears appropriate for their job description.

In fact, I'm not sure your code is entirely portable. If I'm not
mistaken it appears that you're assuming that all character sets have
alphabetical glyphs with contiguous values (i.e. they could do
aAbBcCdD... as far as I'm aware).

But it just goes to show how stupidly tricky such a problem is. I
really am going to have to peel the characters off in to 2-digit
buffers and read from those :/

Kind regards,

Edd
 
J

Jacek Dziedzic

Hello all,

I'm trying to put together some code that reads 3 adjacent pairs of hex
digits from an istream. Each hex digit pair represents a non-negative
integer.

For example I would like to extract 1, 255 and 18 from 01ff12 (as the
components of a simple RGB colour, in my application).

I was hoping to find something like std::setw for istreams and use it
in combination with std::hex so that I could read 2 hex digits at a
time, but as far as I can tell no such entity exists in the C++
standard library.
Is there a neat and tidy way of doing what I want, or am I going to
have to do something nasty like put each digit pair in to its own
buffer and stream-extract/strtoul the numbers from there?

Why is it that no equivalent to setw exists for istreams?

How about reading all six digits as one number from the stream,
using std::hex and then extracting the three components using
the modulo operation?

That would save you the two-digit buffer but probably you
would run into little/big-endian issues.

HTH,
- J.
 
P

Peter Jansson

Hello all,

I'm trying to put together some code that reads 3 adjacent pairs of hex
digits from an istream. Each hex digit pair represents a non-negative
integer.

For example I would like to extract 1, 255 and 18 from 01ff12 (as the
components of a simple RGB colour, in my application).

I was hoping to find something like std::setw for istreams and use it
in combination with std::hex so that I could read 2 hex digits at a
time, but as far as I can tell no such entity exists in the C++
standard library.
Is there a neat and tidy way of doing what I want, or am I going to
have to do something nasty like put each digit pair in to its own
buffer and stream-extract/strtoul the numbers from there?

Why is it that no equivalent to setw exists for istreams?

Kind Regards,

Edd

Hello,

Another approach may be to use std::sscanf from <cstdio>:

#include <cstdio>
#include <iostream>
int main()
{
const char* hex("01ff12");
unsigned int R,G,B;
const int n(std::sscanf(hex,"%2x%2x%2x",&R,&G,&B));
if(3!=n)
std::cerr<<n<<" items assigned, it is supposed to be 3.\n";
else
std::cout<<"R="<<R<<",G="<<G<<",B="<<B<<'\n';
}

On my implementation this gives:
R=1,G=255,B=18


Sincerely,

Peter Jansson
http://www.p-jansson.com/
http://www.jansson.net/
 
E

edd

Hi Peter,

Peter said:
Hello,

Another approach may be to use std::sscanf from <cstdio>:

#include <cstdio>
#include <iostream>
int main()
{
const char* hex("01ff12");
unsigned int R,G,B;
const int n(std::sscanf(hex,"%2x%2x%2x",&R,&G,&B));
if(3!=n)
std::cerr<<n<<" items assigned, it is supposed to be 3.\n";
else
std::cout<<"R="<<R<<",G="<<G<<",B="<<B<<'\n';
}

On my implementation this gives:
R=1,G=255,B=18

Thanks very much for your response. That's certainly the most concise
way of doing things. However, I'm doing this in a teaching context and
I really don't want to start preaching about the benefits of the type
safety in C++ and then use code that ignores them.

The other niggle is that the interface to the function in the example
code takes an std::istream reference. So before using sscanf I'd have
to read the 6 characters in to a buffer, checking each was a hex digit
before reading the next in order to stay true to how the pre-defined
istream extraction operators work.

Would it be possible to write a custom manipulator to do something like
setw for istreams?

I guess I'm just a bit frustrated that this is one of those times where
I'll have to admit that C++ is deficient in some sense, even though the
task isn't really all that complicated.

Edd
 
D

Denise Kleingeist

Hello Edd!
I was hoping to find something like std::setw for istreams and use it
in combination with std::hex so that I could read 2 hex digits at a
time, but as far as I can tell no such entity exists in the C++
standard library.

It is not readily there but it can be built relatively easily into
a suitable num_get facet (code see below).
Is there a neat and tidy way of doing what I want, or am I going to
have to do something nasty like put each digit pair in to its own
buffer and stream-extract/strtoul the numbers from there?

I would prefer something like the num_get facet described
above...
Why is it that no equivalent to setw exists for istreams?

I don't know about the reasons but those I'd consider to be
most likely could be seen as an insult on some of the more
active committee members. Thus, I'm keeping them for
myself...

Good luck, Denise!
--- CUT HERE ---
#include <iostream>
#include <iomanip>
#include <locale>

class width_num_get:
public std::num_get<char>
{
struct countbuf:
std::streambuf
{
countbuf(std::istreambuf_iterator<char> beg,
std::istreambuf_iterator<char> end, int count):
m_beg(beg), m_end(end), m_count(count)
{
}

int underflow()
{
if (this->m_beg == this->m_end || this->m_count == 0)
return traits_type::eof();
--this->m_count;
this->m_buffer = *this->m_beg++;
this->setg(&m_buffer, &m_buffer, &m_buffer + 1);
return traits_type::not_eof(this->m_buffer);
}

std::istreambuf_iterator<char> m_beg, m_end;
int m_count;
char m_buffer;
};

private:
iter_type
do_get(iter_type it, iter_type end, std::ios_base& fmt,
std::ios_base::iostate& state, long& val) const
{
return this->get_template(it, end, fmt, state, val);
}
iter_type
do_get(iter_type it, iter_type end, std::ios_base& fmt,
std::ios_base::iostate& state, unsigned short& val) const
{
return this->get_template(it, end, fmt, state, val);
}

iter_type
do_get(iter_type it, iter_type end, std::ios_base& fmt,
std::ios_base::iostate& state, unsigned int& val) const
{
return this->get_template(it, end, fmt, state, val);
}

iter_type
do_get(iter_type it, iter_type end, std::ios_base& fmt,
std::ios_base::iostate& state, unsigned long& val) const
{
return this->get_template(it, end, fmt, state, val);
}


template <typename T>
iter_type
get_template(iter_type it, iter_type end, std::ios_base& fmt,
std::ios_base::iostate& state, T& val) const
{
if (fmt.width() == 0)
return this->std::num_get<char>::do_get(it, end, fmt, state,
val);
countbuf sbuf(it, end, fmt.width(0));

this->std::num_get<char>::do_get(std::istreambuf_iterator<char>(&sbuf),
std::istreambuf_iterator<char>(),
fmt, state, val);
state &= ~std::ios_base::eofbit;
return sbuf.m_beg;
}
};

int main()
{
std::cin.imbue(std::locale(std::locale(), new width_num_get));
for (short val; std::cin >> std::hex >> std::setw(2) >> val; )
std::cout << "value: 0x" << std::hex << val << "\n";
}
 
E

edd

Hello Denise,

Denise said:
I don't know about the reasons but those I'd consider to be
most likely could be seen as an insult on some of the more
active committee members. Thus, I'm keeping them for
myself...

Fair enough :)

[ generous code snipped ]

Thanks so much for your code, Denise! I have never used facets before
(explicitly) so I have to admit I don't fully understand it yet. I'm
doing some more reading now!

I'm not sure that code of such a technical nature will make it in to
the teaching notes, but I'm very happy to see that there is a C++
solution! Perhaps I'll include it as an appendix.

At any rate, thank you very much for taking the time to put that
together!

Kind regards,

Edd
 
P

P.J. Plauger

I don't know about the reasons but those I'd consider to be
most likely could be seen as an insult on some of the more
active committee members. Thus, I'm keeping them for
myself...

Probably just as well. In this particular case, the committee did
something rather revolutionary -- it codified existing practice.
Between 1990 and 1997 nobody seems to have complained that the
iostreams package developed in the 1980s was deficient in this
regard, so oddly enough the committee did nothing to change it.

Go figure.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
D

Denise Kleingeist

Hello P.J.!
P.J. Plauger said:
Probably just as well. In this particular case, the committee did
something rather revolutionary -- it codified existing practice.

This is indeed revolutionary! ... considering that IOStream was always
intended as a replacement for stdio and thus should have the same
power, including for the task of parsing input. Someone should have
noted that the existing practice was incomplete and inconsistent
(setw has an effect when reading to a char*).
Between 1990 and 1997 nobody seems to have complained that the
iostreams package developed in the 1980s was deficient in this
regard, so oddly enough the committee did nothing to change it.

There were definitely compaints about the absence of setw support
for istreams (e.g.
<http://groups.google.com/group/comp.std.c++/msg/3cb7faa13ef4d70c>).
Also, at this time there were many experts who strongly argued that
stdio should still be used in C++, e.g. because the efficiency of
widely used implementations of IOStreams was mediocre with
implementers publically arguing that it can't be done differently.
Thus,
many programmers did not use IOStreams and thus didn't realize the
true but correctable deficencies.

As things are for programming languages, the standardization is to
a fair extend setting up precedence on how to use a programming
language. This includes that things often go beyond existing practice
for the benefit of future ease of use. Supporting something like setw
to cover the capabilities of C's stdio input facilities would clearly
fall into this area.

Good luck, Denise!
 
P

P.J. Plauger

Hello P.J.!


This is indeed revolutionary! ... considering that IOStream was always
intended as a replacement for stdio and thus should have the same
power, including for the task of parsing input. Someone should have
noted that the existing practice was incomplete and inconsistent
(setw has an effect when reading to a char*).

Perhaps someone should have, but nobody saw fit to tell the committee.
There were definitely compaints about the absence of setw support
for istreams (e.g.
<http://groups.google.com/group/comp.std.c++/msg/3cb7faa13ef4d70c>).

So you think a comment in a newsgroup is sufficient to cause an
overworked committee to undertake a work item to do something
about it? Try spending some time on a standards committee and
see whether your perspective changes.
Also, at this time there were many experts who strongly argued that
stdio should still be used in C++, e.g. because the efficiency of
widely used implementations of IOStreams was mediocre with
implementers publically arguing that it can't be done differently.

I still argue that stdio should be used where it's superior, but
then I'm definitely a pragmatist among idealists in this regard.
Thus,
many programmers did not use IOStreams and thus didn't realize the
true but correctable deficencies.

And so the committee should have been prescient, in the absence
of concrete requests?
As things are for programming languages, the standardization is to
a fair extend setting up precedence on how to use a programming
language. This includes that things often go beyond existing practice
for the benefit of future ease of use. Supporting something like setw
to cover the capabilities of C's stdio input facilities would clearly
fall into this area.

Fine. And who took the trouble to point this out?

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
DewittMill
Top