Jump to content
  • Advertisement
Sign in to follow this  
crocomire

snprintf c++ equivalent

This topic is 4702 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

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::string

int 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


Link to 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


Link to 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


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

Share this post


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

Share this post


Link to 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


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!