"Type-safe" sprintf

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



Hello!

I have invented a simple sprintf-like function aimed at replacing sprintf.
The reason being that our code has a lot of dangerous sprintf's that nobody
here is interested in re-writing.

Here is the implementation of my sprintf-like function. It builds on
boost::format. This function is typesafe and provides error checking. It
will throw an exception if the output buffer is overrun, or there weren't
enough arguments.

What do you think?

#include <iostream>
#include <algorithm> // copy
#include <stdexcept> // std::logic_error
#include <string> // std::basic_string
#include <boost/format.hpp> // boost::basic_format

namespace cool
{
namespace detail
{
template<class Char>
class feeder
{ };

template<class Char, std::size_t N>
class feeder<Char[N]>
{
typedef typename boost::basic_format<Char> format_type;
typedef typename std::basic_string<Char> string_type;

format_type format;
Char* buffer;
bool first;

public:

feeder(Char* b)
: buffer(b),
first(true)
{ }

~feeder()
{
// I know it is bad to throw from destructor...
string_type result = str(format);
if( result.size()>=N )
throw std::logic_error("too many arguments to sprintf");
std::copy(result.begin(), result.end(), buffer);
buffer[result.size()] = 0;
}

//! Might be the format string
feeder& operator%(const Char* c)
{
if( first )
{
format.parse(c);
first = false;
}
else
{
format % c;
}

return *this;
}

//! Can never be the format string
template<class T>
feeder& operator%(const T& c)
{
if( first )
{
throw std::logic_error("bad format string");
}
else
{
format % c;
}

return *this;
}
};
}

template<class Source>
typename detail::feeder<Source> sprintf(Source& buffer)
{
typedef typename detail::feeder<Source> Feeder;
return Feeder(buffer);
}
}

int main()
{
char buffer[1024];

cool::sprintf(buffer) % "%d.%d" % 5 % 1;
std::cout << "buffer=" << buffer << std::endl;

char temp[1024];
const int triangle_number = 15;
cool::sprintf(temp) % "71....+TRI%013d " % triangle_number;
std::cout << "temp=" << temp << std::endl;

try
{
cool::sprintf(temp) % "71....+TRI%013d ";
}
catch( std::exception& ex )
{
std::cerr << ex.what() << std::endl;
}

wchar_t wideBuffer[10];
cool::sprintf(wideBuffer) % L"%d.%d" % 5 % 1;
std::wcout << "wideBuffer=" << wideBuffer << std::endl;

return 0;
}

This prints:
buffer=5.1
temp=71....+TRI0000000000015
boost::too_few_args: format-string refered to more arguments than were
passed
wideBuffer=5.1

--
Daniel
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
.



Relevant Pages

  • Re: "Type-safe" sprintf
    ... I have invented a simple sprintf-like function aimed at replacing sprintf. ... Char* buffer; ... Might be the format string ... feeder& operator% ...
    (microsoft.public.vc.language)
  • Re: Here is my full program excluding the search and delete functions
    ... There is a mismatch between your format specifications and the ... &ID has type pointer to array of 50 int, ... %s requires a char*. ... >empty, empty); ...
    (comp.lang.c)
  • Re: structure
    ... > char a:4; ... if the compiler does not support ... to match an internal data structure to some external data format ... as the internal representation. ...
    (comp.lang.c)
  • Re: Watcom vs. gcc/icl/cl difference
    ... fputs((const char *)&buffer, echoFP); ... static void out(int opt, const char * format, ...) ... /* Invalid flags given? ...
    (comp.lang.c)