snprintf c++ equivalent

Started by
13 comments, last by SiCrane 18 years, 7 months ago
Hi, Is there a standard c++ equivalent to the c function snprintf?
Advertisement
The C function is still available, but the Right Way to do it in C++ is using iostreams, which are type-safe and won't be vunerable to buffer overflows. For example:
#include <iostream> // general iostream header#include <sstream>  // for std::stringstream#include <string>   // for std::stringint main(int argc, char *argv[]){    int an_int = 0xf00d;    float a_float = 13.37f;    const char *a_message = "Hello, world!";    std::ostringstream ss;    ss << "The integer is: " << an_int << "\n"          "The float is: " << a_float << "\n"          "The message is: '" << a_message << "'" << std::endl;        // you can access the contents of the stringstream as a std::string, with:    std::string the_result = ss.str();    // and if you absolutely need a null-terminated const char* string, you can use:    the_result.c_str();    return 0;}


John B
The best thing about the internet is the way people with no experience or qualifications can pretend to be completely superior to other people who have no experience or qualifications.
For everything you ever wanted to know about C++ string formatting, read The String Formatters of Manor Farm.
Jooleem. Get addicted.
Since the two major criticisms of stringstreams are their verbosity and lack of efficiency I've never understood why nobody has tried to write a stringstream based solution with functor based short format objects, i.e.:
/* warning hacked code, ruthlessly prematurely optimised (pessimised?) and generally nasty */#include <algorithm>#include <iostream>#include <string>#include <vector>namespace{	unsigned int const right = 0;	unsigned int const left = 1;}class _d{	public:		explicit _d(int value, int width = 0, int align = right)			:			value_(value),			width_(width),			align_(align)		{		}		template < typename TYPE >		std::basic_ostream< TYPE > & print(std::basic_ostream< TYPE > & ostream) const		{			writeToBuffer(value_);			unsigned int padWidth = std::max(width_ - int(numberBuffer_.size()), 0);//			fillPaddingBuffer(padWidth);			if (align_ == right)			{//				std::copy(paddingBuffer_.begin(), paddingBuffer_.begin() + padWidth, std::ostreambuf_iterator< char >(ostream));				std::fill_n(std::ostreambuf_iterator< char >(ostream)), padWidth, ' ');			}			std::copy(numberBuffer_.rbegin(), numberBuffer_.rend(), std::ostreambuf_iterator< char >(ostream));			if (align_ == left)			{//				std::copy(paddingBuffer_.begin(), paddingBuffer_.begin() + padWidth, std::ostreambuf_iterator< char >(ostream));				std::fill_n(std::ostreambuf_iterator< char >(ostream)), padWidth, ' ');			}			return ostream;		}	private:/*		static void fillPaddingBuffer(unsigned int size)		{			if (paddingBuffer_.size() < size)			{				paddingBuffer_.insert(paddingBuffer_.end(), size - paddingBuffer_.size(), ' ');			}		}*/		static void writeToBuffer(int value)		{			std::vector< char >::iterator bufferPosition = numberBuffer_.begin();			unsigned int uvalue;			if (value < 0)			{				uvalue = -value;			}			else			{				uvalue = value;			}			while (uvalue > 0 && bufferPosition != numberBuffer_.end())			{				*bufferPosition = (uvalue % 10) + '0';				uvalue /= 10;				++bufferPosition;			}			if (uvalue == 0)			{				if (bufferPosition != numberBuffer_.end())				{					numberBuffer_.resize(bufferPosition - numberBuffer_.begin());				}			}			else			{				while (uvalue > 0)				{					numberBuffer_.push_back((uvalue % 10) + '0');					uvalue /= 10;				}			}			if (value < 0)			{				if (bufferPosition != numberBuffer_.end())				{					*bufferPosition = '-';					++bufferPosition;				}				else				{					numberBuffer_.push_back('-');				}			}		}		int value_;		int width_;		int align_;		static std::vector< char > numberBuffer_;//		static std::vector< char > paddingBuffer_;};std::vector< char > _d::numberBuffer_;//std::vector< char > _d::paddingBuffer_;class _s{	public:		explicit _s(std::string value, int width = 0, int align = right)			:			value_(value),			width_(width),			align_(align)		{		}		template < typename TYPE >		std::basic_ostream< TYPE > & print(std::basic_ostream< TYPE > & ostream) const		{			unsigned int padWidth = std::max(width_ - int(value_.size()), 0);//			fillPaddingBuffer(padWidth);			if (align_ == right)			{//				std::copy(paddingBuffer_.begin(), paddingBuffer_.begin() + padWidth, std::ostreambuf_iterator< char >(ostream));				std::fill_n(std::ostreambuf_iterator< char >(ostream)), padWidth, ' ');			}			ostream << value_;			if (align_ == left)			{//				std::copy(paddingBuffer_.begin(), paddingBuffer_.begin() + padWidth, std::ostreambuf_iterator< char >(ostream));				std::fill_n(std::ostreambuf_iterator< char >(ostream)), padWidth, ' ');			}			return ostream;		}	private:/*		static void fillPaddingBuffer(unsigned int size)		{			if (paddingBuffer_.size() < size)			{				paddingBuffer_.insert(paddingBuffer_.end(), size - paddingBuffer_.size(), ' ');			}		}*/		std::string value_;		int width_;		int align_;//		static std::vector< char > paddingBuffer_;};//std::vector< char > _s::paddingBuffer_;template < typename TYPE >std::basic_ostream< TYPE > & operator<<(std::basic_ostream< TYPE > & ostream, _d const & d){	return d.print(ostream);}template < typename TYPE >std::basic_ostream< TYPE > & operator<<(std::basic_ostream< TYPE > & ostream, _s const & s){	return s.print(ostream);}int main(){	int number = 123456789;	std::string string = "little";	std::cout << "Here is a number-" << _d(number, 4) << "-and a-" << _s(string, 10) << "-word\n";	number = -5;	std::cout << "Here is a number-" << _d(number, 4, left) << "-and a-" << _s(string, 10, left) << "-word\n";	number = 5;	std::cout << "Here is a number-" << _d(number, 4, right) << "-and a-" << _s(string, 10, left) << "-word\n";}

Obviously this tramples on the implementor's names (but then so does boost with it's _1 etc.) and ignores formatting flags on the stream and you'd have to give up positional parameters, but apart from that, shouldn't this approach give the best of all worlds? Efficient (minimal dynamic memory allocation), concise (except for spelling out "left" and "right" and adding commas. '_l' and '_r' perhaps?), easy to use (no need to manage memory yourself), length safe, type safe, doesn't separate formatting information from the actual object being formatted and (with the obvious extension of making _d and _s template functions which return templated functors and adding a generic functor which doesn't convert its argument to the specified type) useable in templates.

Enigma

EDIT: a correction (I meant to say streams in general, not stringstreams in the first paragraph) and an improvement to the code (I always forget the fill_n is a part of the SC++L. Just because copy_n is non-standard I always think that fill_n is as well).

[Edited by - Enigma on September 5, 2005 7:41:36 AM]
Besides the stringstreams solution there's also:

std::string text = str( boost::format( "(%d,%d)" ) % x % y ); //printf-style formatters
std::string more_text = str( boost::format( "%2% %1%" ) % "world!" % "Hello," ); //explicit argument order specifier (foramts to "Hello, world!")
Quote:Original post by Enigma
Obviously this tramples on the implementor's names (but then so does boost with it's _1 etc.)


WRONG, boost does NOT trample implementor names with _1 and the like in Boost.Lambda. Identifiers starting with a double underscore (or even containing for "__"), an underscore followed by an uppercase letter in any scope, or a lowercase letter in the file scope, are reserved for the implementation. However, boost::_1 does not fit this pattern, it's identifier is an underscore followed by a numerical digit. Ergo it's very poor reasoning to use such an excuse as "well, they did it too!" when they in fact did not do it too. Besides, two wrongs does not make a right.
Quote:Original post by MaulingMonkey
Quote:Original post by Enigma
Obviously this tramples on the implementor's names (but then so does boost with it's _1 etc.)


WRONG, boost does NOT trample implementor names with _1 and the like in Boost.Lambda. Identifiers starting with a double underscore (or even containing for "__"), an underscore followed by an uppercase letter in any scope, or a lowercase letter in the file scope, are reserved for the implementation. However, boost::_1 does not fit this pattern, it's identifier is an underscore followed by a numerical digit. Ergo it's very poor reasoning to use such an excuse as "well, they did it too!" when they in fact did not do it too. Besides, two wrongs does not make a right.

Ah. my mistake. I simplified the rule in my head to "all identifiers starting with an underscore or containing a double underscore are reservered for the implementation". I only used _d and _s because it was intended as an example of an idealised system (in which such names would be legal [grin]). So just replace _d and _s with d_ and s_ respectively.

Enigma
You say apple, I say food processor Mk. 2003. I have an unimaginable hatred for all things underscore-differentiated (by which I mean _foo instead of foo, bar_ instead of bar, and _i_kill_you_ instead of i_kill_you) which is why I sick the standard on you :-p.
Quote:Original post by Peregrin
For everything you ever wanted to know about C++ string formatting, read The String Formatters of Manor Farm.


Why is it that boost::format is inexplicably missing in there?
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Because it was written four years ago.

This topic is closed to new replies.

Advertisement