Convert an integer to a string? Plan B?

  • Thread starter Steven T. Hatton
  • Start date
M

mlimber

Steven said:
http://public.research.att.com/~bs/bs_faq2.html#int-to-string

Is there no C library function that will take an int and convert it to its
ascii representation? The example Bjarne shows in his faq is not extremely
convenient.

The example Stroustrup gives is the best way. Compare:

http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.2
http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.3

As the FAQ notes, you could use C facilities rather than C++, but the
latter should be preferred.

See also Boost's lexical_cast.

Cheers! --M
 
P

Pete Becker

mlimber said:
As the FAQ notes, you could use C facilities rather than C++, but the
latter should be preferred.

However, it doesn't say why the "C++" approach should be preferred.
(Yes, I'm aware that it's possible to pass too small an output buffer to
the C functions. Don't do that.)

C provides perfectly good conversion functions. The "C++" approach,
creating a stringstream object which you then throw away, is extremely
expensive. I would never use it.
 
S

Steven T. Hatton

mlimber said:
The example Stroustrup gives is the best way. Compare:

http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.2
http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.3

As the FAQ notes, you could use C facilities rather than C++, but the
latter should be preferred.

See also Boost's lexical_cast.

Cheers! --M

The whole idea is to have one little call such as itos there when you need
it. I usually want to do it in a situation such as :

virtual const char* what() const throw() {
std::string message(_myName);
message+="\n\t";
message+=""!=_namespaceName? _namespaceName+"::":"";
message+=""!=_className ? _className +"::":"";

message+=""!=_file?" File: "+_file :"";
message+= 0!=_line?" Line: "+itos(_line):"";

message+=_message;

return message.toStdString().c_str();
}
Note that I have not yet tested this with std::string. I am converting code
that used Qt QString, which provides a QString::setNum(int).
 
D

Dietmar Kuehl

Pete said:
C provides perfectly good conversion functions.

.... which are harder to use than might seem. Both the buffer size
and the conversion specifiers are potential stumbling blocks. Of
course, any experienced C programmers knows these.
The "C++" approach,
creating a stringstream object which you then throw away, is extremely
expensive. I would never use it.

It is actually a shame that there is no simple and efficient method
addressing this issue. Of course, what actually qualifies as simple
and/or efficient depends on the user's view. I would use something
like the code below:

#include <algorithm>
#include <limits>

template <typename V, typename OutIt>
OutIt convert(V v, OutIt to)
{
enum { bsize = std::numeric_limits<V>::digits10 + 2 };
bool neg = v < 0;

char buffer[bsize], *tmp = buffer + bsize;
*--tmp = "0123456789"[(neg? -(v + (v < -10? 10: 0)): v) % 10];
v /= 10;

if (neg)
v = -v;

for (; v > 0; v /= 10)
*--tmp = "0123456789"[v % 10];

if (neg)
*--tmp = '-';
return std::copy(tmp, buffer + bsize, to);
}

Of course, when used with an inappropriate iterator it also gives
the potential of a buffer overrun. ... but it also allows for
perfectly save use e.g. when using 'std::back_inserter()' together
with an 'std::string' as the destination.
 
D

Daniel T.

"Steven T. Hatton said:
http://public.research.att.com/~bs/bs_faq2.html#int-to-string

Is there no C library function that will take an int and convert it to its
ascii representation? The example Bjarne shows in his faq is not extremely
convenient.

How could it be any more convenient?

template < typename T, typename U >
T lexical_cast( const U& u ) {
std::stringstream ss;
T t;
if ( !( ss << u && ss >> t ) ) throw std::bad_cast();
return t;
}

The above can be used exactly like any of the other casting operators
that C++ provides. Boost provides a much more optimized version if you
find it necessary.
 
D

Daniel T.

Pete Becker said:
However, it doesn't say why the "C++" approach should be preferred.
(Yes, I'm aware that it's possible to pass too small an output buffer to
the C functions. Don't do that.)

Passing a too small buffer, and type safety are the two reasons I can
think of off hand, there may be more.

When it comes down to it, the C++ approach has less undefined behavior.
In order to "just don't do that" one must know all the things that one
shouldn't do. Obviously, the fewer things there are, the easer it will
be to not do them.

C provides perfectly good conversion functions.

"perfectly"? I think "adequate" would be a more appropriate adjective,
as in "barely sufficient or satisfactory". But that's just me, YMMV.

The "C++" approach,
creating a stringstream object which you then throw away, is extremely
expensive. I would never use it.

I use stringstreams for this purpose all over my code and I've never had
a user complain that it's too slow because of it. Yes, I've written code
that's "too slow", that's part of the nature of writing games, but
profiling showed other parts of the code that were far more expensive
than stringstreams.
 
B

Bo Persson

Steven T. Hatton said:
The whole idea is to have one little call such as itos there when
you need
it. I usually want to do it in a situation such as :

virtual const char* what() const throw() {
std::string message(_myName);
message+="\n\t";
message+=""!=_namespaceName? _namespaceName+"::":"";
message+=""!=_className ? _className +"::":"";

message+=""!=_file?" File: "+_file :"";
message+= 0!=_line?" Line: "+itos(_line):"";

message+=_message;

return message.toStdString().c_str();
}
Note that I have not yet tested this with std::string.

No, obviously! :)

This is exactly why using C style char pointers are so dangerous. As
soon as the function returns, the message goes out of scope, and is
destroyed.

Guess what happens to the pointer?


(You also cannot say that what() doesn't throw, when any of the string
operations might throw a bad_alloc.)


Bo Persson
 
P

Pete Becker

Daniel said:
Passing a too small buffer,

Yup. You have to know something about conversions to get conversions right.
and type safety are the two reasons I can
think of off hand,

We're talking about converting an int. Write it once, get it right, end
of discussion.
there may be more.

There may not be. Doesn't affect this discussion.
When it comes down to it, the C++ approach has less undefined behavior.

Nope. Write it once, get it right. No undefined behavior.

Oh, you meant "less opportunity to screw up if you're careless". Well,
that's true. Programming isn't a job for careless people.
In order to "just don't do that" one must know all the things that one
shouldn't do. Obviously, the fewer things there are, the easer it will
be to not do them.

Easier isn't the only goal, especially when any competent programmer
ought to be able to convert an int to a string with very little effort.
I use stringstreams for this purpose all over my code and I've never had
a user complain that it's too slow because of it. Yes, I've written code
that's "too slow", that's part of the nature of writing games, but
profiling showed other parts of the code that were far more expensive
than stringstreams.

Gratuitously slow code isn't good design. The reasons for using a
stringstream simply aren't compelling, and the cost is high. If you
don't put the slow stuff in, you won't have to take it out later.
 
S

Steven T. Hatton

Daniel said:
How could it be any more convenient?

template < typename T, typename U >
T lexical_cast( const U& u ) {
std::stringstream ss;
T t;
if ( !( ss << u && ss >> t ) ) throw std::bad_cast();
return t;
}

The above can be used exactly like any of the other casting operators
that C++ provides. Boost provides a much more optimized version if you
find it necessary.

It's just one of those little things I would expect to have in the Standard
Library which isn't there. It's good to know it's in boost. At least that
is a more or less standard resource.
 
S

Steven T. Hatton

Bo said:
No, obviously! :)

This is exactly why using C style char pointers are so dangerous. As
soon as the function returns, the message goes out of scope, and is
destroyed.

Guess what happens to the pointer?

Well, the alternative is to have message be a class member that goes out of
scope at some unpredictable point in the user's environment. Technically,
I believe the c_str() data is fair game if it is used immediately upon
return of the what() function.
(You also cannot say that what() doesn't throw, when any of the string
operations might throw a bad_alloc.)

I guess I will have to swallow such an exception, and hope it doesn't kill
me. I could use all C string functions, but that would simply be evading
the issue. If I do happen to get a bad_alloc I'm hosed anyway. Perhaps I
just do a catch(const std::bad_alloc& ba) { return ba.what(); }
 
C

Christian Gollwitzer

Steven T. Hatton wrote
The whole idea is to have one little call such as itos there when you need
it. I usually want to do it in a situation such as :

virtual const char* what() const throw() {
std::string message(_myName);
message+="\n\t";
message+=""!=_namespaceName? _namespaceName+"::":"";
message+=""!=_className ? _className +"::":"";

message+=""!=_file?" File: "+_file :"";
message+= 0!=_line?" Line: "+itos(_line):"";

message+=_message;

return message.toStdString().c_str();
}
Note that I have not yet tested this with std::string. I am converting code
that used Qt QString, which provides a QString::setNum(int).

Exactly this is what stringstreams are invented for!!

int some_integer=125;
std::eek:stringstream os;
os <<"\n\t"
<< ""!=_namespaceName? _namespaceName+"::":""
<< some_integer
<< _message;
return os.str();

You don't need a special conversion routine here, since the automatic
conversion from ints to strings is done by the magic of streams.
 
S

Steven T. Hatton

Christian said:
Steven T. Hatton wrote

Exactly this is what stringstreams are invented for!!

int some_integer=125;
std::eek:stringstream os;
os <<"\n\t"
<< ""!=_namespaceName? _namespaceName+"::":""
<< some_integer
<< _message;
return os.str();

You don't need a special conversion routine here, since the automatic
conversion from ints to strings is done by the magic of streams.

In this case it is true. However, in the other parts of the code I make the
conversion in a function call.
 
A

andy

Pete said:
Yup. You have to know something about conversions to get conversions right.

This is worrying. What about the C++ aim to help occasional and novice
programmers? (which is very relevent to this newsgroup BTW)?

To be portable you would either have to #ifdef dependent on platform or
make sure that your buffer would hold the largest possible int at least
till it gets bigger. I assume that your own stringent performance
standards wouldnt consider allowing you to waste stack space so you'd
have to use some sort of sizeof and then calculate either manually or
in code which means checking its correct, bearing in mind terminators,-
signs etc, similarly write a long et al version too and try to call the
right version (see below). In fact its pretty tedious to do all that
and check its correct and now what are you going to do with the char
array result anyway? Why bother? Let an ostringstream do the work and
spend brain power on more important things.

The other major advantage (apart from the very important one of
automatic resource management) of a stringstream is that you can make
the integer type a template parameter, which means it beats your write
once rule as using C functions you would need to rewrite for long and
unsigned long, double and float, all which may have different lengths
of ascii representation afaik.
We're talking about converting an int. Write it once, get it right, end
of discussion.

Then do same for a long, an unsigned long, a float, a double etc etc...
Then check all your buffer lengths... etc etc.
There may not be. Doesn't affect this discussion.


Nope. Write it once, get it right. No undefined behavior.

Oh, you meant "less opportunity to screw up if you're careless". Well,
that's true. Programming isn't a job for careless people.

If you have never made a careless mistake in your code then you must be
exceptional, but if only people that have never made coding mistakes
are allowed to code C++ I think the language is dead and you may soon
be the only C++ programmer. No offence to anyone else that has never
made a typo intended of course.

Of course then there would be no need for diagnostics from the compiler
as you and the other perfect programmer dont need them.

Again what about making the language accessible to occasional and
novice programmers?
Easier isn't the only goal, especially when any competent programmer
ought to be able to convert an int to a string with very little effort.

Easier and catching mistakes are very important goals. Thats basically
what high level programming is all about.

I use them too. They are very convenient IMO.

regards
Andy Little
 
P

Pete Becker

This is worrying. What about the C++ aim to help occasional and novice
programmers? (which is very relevent to this newsgroup BTW)?

There is a proposal to provide simple, efficient conversion functions.
To be portable you would either have to #ifdef dependent on platform or
make sure that your buffer would hold the largest possible int at least
till it gets bigger.

That's what INT_MAX, etc. are for.
I assume that your own stringent performance
standards wouldnt consider allowing you to waste stack space so you'd
have to use some sort of sizeof and then calculate either manually or
in code which means checking its correct, bearing in mind terminators,-
signs etc, similarly write a long et al version too and try to call the
right version (see below). In fact its pretty tedious to do all that
and check its correct and now what are you going to do with the char
array result anyway? Why bother? Let an ostringstream do the work and
spend brain power on more important things.

This is a big problem in programming today: so many applications are so
big and slow.
The other major advantage (apart from the very important one of
automatic resource management) of a stringstream is that you can make
the integer type a template parameter, which means it beats your write
once rule as using C functions you would need to rewrite for long and
unsigned long, double and float, all which may have different lengths
of ascii representation afaik.




Then do same for a long, an unsigned long, a float, a double etc etc...
Then check all your buffer lengths... etc etc.

No, the question was about converting an integer.
If you have never made a careless mistake in your code then you must be
exceptional, but if only people that have never made coding mistakes
are allowed to code C++ I think the language is dead and you may soon
be the only C++ programmer.

I've certainly made my share of careless mistakes. The way to eliminate
them is to catch them with appropriate tests, not write big, slow code
in the hope that it will overwhelm them.
 
A

andy

Pete said:
This is a big problem in programming today: so many applications are so
big and slow.

Reducing size of a C++ application really needs standardisation of
DLL's. The standard library is not large enough or comprehensive enough
so everyone is always reinventing the wheel. Combine large standard
library and put parts in DLL's would have a great impact on size at
least. Smaller apps need to swap to disk less so might even help speed
too.

regards
Andy Little
 
D

Daniel T.

Pete Becker said:
This is a big problem in programming today: so many applications are so
big and slow.

That doesn't mean they are big and slow because stringstream is used for
conversions. Nice straw man there.

No, the question was about converting an integer.

You've got me there. So many programers need to convert int to strings,
but no one never needs to convert any other type to a string...

I've certainly made my share of careless mistakes. The way to eliminate
them is to catch them with appropriate tests, not write big, slow code
in the hope that it will overwhelm them.

So, what is the "appropriate test" to ensure that a buffer is not
overrun? What is an "appropriate test" to ensure that the type matches
the "type" in the char array passed into sprintf?

The fact is, writing passed the end of a buffer causes undefined
behavior, therefore there is *no* way to test it because there is no
defined result to test for.
 
P

Pete Becker

Daniel said:
That doesn't mean they are big and slow because stringstream is used for
conversions.

There is a great deal more in the paragraph that I quoted than the
mention of ostringstream. Applications are often big and slow because of
the analysis that ends with "why bother".
You've got me there. So many programers need to convert int to strings,
but no one never needs to convert any other type to a string...

When someone asks about how to convert an integer to a string, it's
overkill to design an enter numeric formatting package.
So, what is the "appropriate test" to ensure that a buffer is not
overrun?

Check its size before you write to it. If you can't do that then you may
have a design error. In the case of a function that formats an integer
value into a local character array, ensuring that the array is large
enough is trivial.
What is an "appropriate test" to ensure that the type matches
the "type" in the char array passed into sprintf?

How did sprintf get into this? Anyway, if you use sprintf, the way you
check it is you inspect the code, or you use a compiler that checks the
argument types against the format specifiers.
The fact is, writing passed the end of a buffer causes undefined
behavior, therefore there is *no* way to test it because there is no
defined result to test for.

First, the fact that the behavior is undefined doesn't mean that there
is no way to test for it. It means that there may not be an obvious,
portable way to test for it. In practice, there are many ways to do
those tests. Debugging implementations have been doing this for years.

Second, you don't test for writing past the end of a buffer. You test
that your buffer is large enough to hold the result, or you prove ab
initio that it is.
 
R

Roland Pibinger

Here's a proposal for a simple, efficient, and safe conversion
function:

#include <stdio.h>
#include <string>

inline std::string& itostr (int i, std::string& out) {
std::string::value_type buf[128];
int len = snprintf(buf, sizeof(buf), "%d", i);

if (len > 0 && size_t (len) < sizeof (buf)) {
out.assign(buf, std::string::size_type (len));
} else {
out.clear();
}
return out;
}

Regards,
Roland Pibinger
 

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

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,196
Latest member
ScottChare

Latest Threads

Top