snprintf c++ equivalent

This topic is 4485 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, Is there a standard c++ equivalent to the c function snprintf?

Share this post

Share on other sites
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

Share this post

Share on other sites
For everything you ever wanted to know about C++ string formatting, read The String Formatters of Manor Farm.

Share this post

Share on other sites
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]

Share this post

Share on other sites
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!")

Share this post

Share on other sites
Quote:
 Original post by EnigmaObviously 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.

Share this post

Share on other sites
Quote:
Original post by MaulingMonkey
Quote:
 Original post by EnigmaObviously 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

Share this post

Share on other sites
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.

Share this post

Share on other sites
Quote:
 Original post by PeregrinFor 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?

Share this post

Share on other sites
Because it was written four years ago.

Share this post

Share on other sites
Well the underscores were my attempt at very short functor names without unnecessary name trampling (or making the whole thing redundant by requiring a namespace prefix). If you just used d and s then eventually you're going to want to support i and Wham! there goes a very common (but bad IMHO) loop variable name. It's a question of practicality.

But regardless of the names used, I'd be more interested in knowing what people think of the concept as a whole. Or does there exist a suitable library which all the qualities listed above (as far as I understand it, boost::format fails on efficiency and partially lacks static typing - by which I mean that the statement boost::format("%d") % std::string("test") will do the wrong thing at runtime rather than failing at compile time as my proposed system could be made to do. It also separates formatting information from the formatted object, which can be a positive or a negative depending on how you look at it. Don't get me wrong, it's a great library, but I want to have my cake and eat it!)

I feel I ought to make explicit that I consider there to be two types of formatting - known static formatting, in which you always want things formatted an exact way, and potentially dynamic formatting where, for example, you may want the format to change at runtime for localisation purposes. My proposal is a solution to the first, boost::format is a solution to both, but lacks efficiency in the first case simply because it is a more general solution.

Enigma

Share this post

Share on other sites
Use boost::format - because if you can't tell for yourself if it's slow or not yourself, it's probably not a performance bottleneck, definately isn't confirmed as one, and is thus a waste of time to be worried about optimizing through the nanoseconds. Especially if your message is only displayed once in the entire program.

I think I've only seen someone complaining about boost::format's performance with an actual example case once on these forums (to format an FPS counter), and if my memory serves, storing the boost::format object so he wasn't reconstructing it hundreds of times a second solved his problem.

EDIT: preformance performance. Grr.

[Edited by - MaulingMonkey on September 5, 2005 3:51:18 PM]

Share this post

Share on other sites
Quote:
 Original post by SiCraneBecause it was written four years ago.

So boost::lexical_cast existed 4 years ago, but format didn't?

Share this post

Share on other sites
Quote:
Original post by Promit
Quote:
 Original post by SiCraneBecause it was written four years ago.

So boost::lexical_cast existed 4 years ago, but format didn't?

Quote:
 From Boost.Format's documentation© Samuel Krempp 2002

(2002 = ~3 years ago)

Quote:
 From Boost.Conversion's lexical_cast documetation© Copyright Kevlin Henney, 2000–2005

(2000 = ~5 years ago)

So I'd say, "probably, yes". The Boost Library Collection is an iterative, ongoing work, after all.

Share this post

Share on other sites
You don't need to look at the docs, you can look at the version history. boost::format wasn't introduced until 10 October 2002.

Share this post

Share on other sites

This topic is 4485 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

• Forum Statistics

• Total Topics
628737
• Total Posts
2984457

• 9
• 25
• 11
• 10
• 16