converting int to const char*

Started by
21 comments, last by jpetrie 14 years, 5 months ago
Quote:Original post by jyk
Quote:This may be useful: itoa()
Is there any particular reason you would recommend itoa() over the previous suggestions, lexical_cast and stringstream? (Note that itoa() is non-standard, and may not even be available to the OP.)


Yes, but it is only alternative, I didnt say it is better. I think effect is same and easy to use, if available.. in every way, you are right, itoa() is non standard
Advertisement
Quote:Original post by stephe
I didnt say it is better. I think effect is same and easy to use, if available.. in every way, you are right, itoa() is non standard

Whether or not it is easy to use depends highly on which version of itoa() is provided, and itoa() is one of the more severely non-standard functions out there. I've run across at least five different function signatures for itoa:
char * itoa(int, char *, int); // you supply the bufferchar * itoa(int, int, char *); // you supply the bufferchar * itoa(int, char *);      // you supply the bufferchar * itoa(int, int);         // you need to free the pointerchar * itoa(int);              // you need to free the pointer

And this ignores the fact that itoa() may in fact be spelled _itoa() depending on your platform. In addition to the variants I've run across myself, I've been told about one more that apparently appeared in very early Unix machines:
void itoa(int input, void (*subr)(char))

This calls the subr function for each character in the resulting string. I believe most people would agree that this would not fall in the "easy to use" category.
I haven't seen this suggested but this may be too simple:

stringstream myString;myString << someInteger;myString.str().c_str();



EDIT:

Whoops... missed the first reply... :S

[Edited by - leeor_net on March 6, 2010 2:47:17 AM]

-Lead developer for OutpostHD

http://www.lairworks.com

Check post #2? :)
Quote:Original post by leeor_net
I haven't seen this suggested but this may be too simple:

stringstream myString;myString << someInteger;myString.str().c_str();


It has been suggested in the first answer given to the OP, which was very concise.


Quote:Original post by dj3hut1
Hello Lith,

you can also use sprintf ( from <cstdio> ).

char buf[80];sprintf( buf, "%d", number );


Possible undefined behaviour if a compiler defines an int to be 34 or more bytes long. Because of that, we use sooner mentioned methods in C++.
Quote:Original post by phresnel
Quote:Original post by dj3hut1
Hello Lith,

you can also use sprintf ( from <cstdio> ).

char buf[80];sprintf( buf, "%d", number );


Possible undefined behaviour if a compiler defines an int to be 34 or more bytes long. Because of that, we use sooner mentioned methods in C++.


Which when it comes down to it that implementation ends up using sprintf or sprintf_s just with a little bit of a smarter buffer size declaration somehow relative to _MAX_INT_DIG from yvals.h
// Full Sail graduate with a passion for games// This post in no way indicates my being awake when writing it
Quote:Original post by dclyde
Quote:Original post by phresnel
Quote:Original post by dj3hut1
Hello Lith,

you can also use sprintf ( from <cstdio> ).

char buf[80];sprintf( buf, "%d", number );


Possible undefined behaviour if a compiler defines an int to be 34 or more bytes long. Because of that, we use sooner mentioned methods in C++.


Which when it comes down to it that implementation ends up using sprintf or sprintf_s just with a little bit of a smarter buffer size declaration somehow relative to _MAX_INT_DIG from yvals.h


_MAX_INT_DIG and yvals.h are non-standard extensions. Also note that _MAX_INT_DIG fails to communicate the base for which an int yields that maximum number of digits.

If for whatever reason you really must use C string functions, then do it at least generic:

#include <climits>// Make *_MAX available for template code.template <typename T> struct get_val_max;template <> struct get_val_max<char> { enum { value = CHAR_MAX }; };template <> struct get_val_max<signed char> { enum { value = SCHAR_MAX }; };template <> struct get_val_max<unsigned char> { enum { value = UCHAR_MAX }; };template <> struct get_val_max<short> { enum { value = SHRT_MAX }; };template <> struct get_val_max<unsigned short> { enum { value = USHRT_MAX }; };template <> struct get_val_max<int> { enum { value = INT_MAX }; };template <> struct get_val_max<unsigned int> { enum { value = UINT_MAX }; };template <> struct get_val_max<long> { enum { value = LONG_MAX }; };template <> struct get_val_max<unsigned long> { enum { value = ULONG_MAX }; };#ifdef LLONG_MAXtemplate <> struct get_val_max<long long> { enum { value = LLONG_MAX }; };template <> struct get_val_max<unsigned long long> { enum { value = ULLONG_MAX }; };#endif
// Check signedness of a type.template <typename T> struct is_signed { enum { value = 1 }; };template <> struct is_signed <char> { enum { value = CHAR_MIN < 0 }; };template <> struct is_signed <unsigned char> { enum { value = 0 }; };template <> struct is_signed <unsigned short> { enum { value = 0 }; };template <> struct is_signed <unsigned int> { enum { value = 0 }; };template <> struct is_signed <unsigned long> { enum { value = 0 }; };#ifdef LLONG_MAXtemplate <> struct is_signed <unsigned long long> { enum { value = 0 }; };#endif
// Recurse and divide by base until zero is reachedtemplate <typename T, T base, T val, bool recurse=val!=0>struct get_dig_max_rec {        enum { value = 1 + get_dig_max_rec<T, base, val/base>::value };};template <typename T, T base, T val> struct get_dig_max_rec<T,base,val,false> {        enum { value = 0 };};


Replacement for non-standard and non-generic _MAX_INT_DIG:
// Get number of digits needed to represent any value of T in basetemplate <typename T, int base> struct get_dig_max {        enum {                value = is_signed<T>::value                      + get_dig_max_rec<T, base, get_val_max<T>::value>::value        };};// some typedefstemplate <typename T> struct get_base10_dig_max : get_dig_max<T,10> {};template <typename T> struct get_base16_dig_max : get_dig_max<T,16> {};template <typename T> struct get_base2_dig_max : get_dig_max<T,2> {};


Test:

#include <iostream>int main () {        std::cout << get_base10_dig_max<char>::value << '\n';        std::cout << get_base10_dig_max<signed char>::value << '\n';        std::cout << get_base10_dig_max<unsigned char>::value << '\n';        std::cout << get_base10_dig_max<unsigned int>::value << '\n';        std::cout << get_base10_dig_max<int>::value << '\n';        // etc.}



So in case of dj3hut1's code:
char buf[get_base10_dig_max<int>::value + 1];

or later when C++0x is here
char buf[get_base10_dig_max<decltype(your_var)>::value + 1];




That way, the code does not and won't yield undefined behaviour, and does not waste memory at the same time.


edit: fixed is_signed, thanks mattd.

[Edited by - phresnel on November 9, 2009 10:50:23 AM]
I think you got the is_signed specializations for unsigned types the wrong way around.
Quote:Original post by mattd
I think you got the is_signed specializations for unsigned types the wrong way around.


Much appreciated :)
I would do something like this:
class ScoreText {public:	ScoreText(int x, int y, TTF_Font * font, SDL_Color color)		: current_score(0)		, font(font)		, cache(NULL)		, color(color)	{		dst.x = x;		dst.y = y;	}	~ScoreText() {		clear_cache();			}	void update(int new_score) {		if (current_score == new_score) return;		current_score = new_score;		clear_cache();	}	void set_position(int x, int y) {		dst.x = x;		dst.y = y;	}	bool render(SDL_Surface * s) {		SDL_Surface * text = get_cached();		if (text != NULL) {			int result = SDL_BlitSurface(text, NULL, s, &dest);			if (result == -2) clear_cache();		}		return result == 0;	}private:	void clear_cache() {		if (cache != NULL) SDL_FreeSurface(cache);		cache = NULL;	}	SDL_Surface * get_cached() {		if (cache == NULL) {			char buf[40];			sprintf(buf, "%d", current_score);			cache = TTF_RenderText_Solid(font, buf, color);		}		return cache;	}	int current_score;	SDL_Rect dest;	SDL_Surface * cache;	SDL_Color color;};


sprintf is ideal choice. We have a well known range, it avoids potential heap allocations, and doesn't require third-party libraries and SDL is a C library, so some 'print_score()' function could be provided as straight C function. In addition, it works on C strings, same as SDL, so there is no need to bring in std::string.

For more generic printing, the alternatives were listed above. Stringstream is a must when several things need to be printed, lexical_cast can be used to work with std::string. printf in general is very clumsy when dealing with straight user input simply due to manual memory management - but this is not the case here.

Good code is primarily about choosing proper compromises. Unless any other constraints are given, stringstream would be generic choice. But this is a common problem today - massive over-engineering without any critical thinking about the actual problem to be solved.

The task here is to Print Score In Pong Game. Score will not be represented with 272 bits. It will probably be 0 to 10.


Instead, rather than writing 500 lines of code to Print a freeking int, going through all the SDL documentation to properly handle all the return codes would be much more productive. When must a surface be freed, what happens if surface is lost (I don't handle this case thoroughly above), which encoding to use, etc... 999:1 chance says that someone will forget to call FreeSurface and leak memory long before their code breaks due to 272-bit integer stack buffer overflow.

This topic is closed to new replies.

Advertisement