String &String::operator<<(int i)
{ *this += i;
return *this;
}
String &String::operator<<(double i)
{ *this += i;
return *this;
}
String &String::operator<<(const char *add)
{ *this += add;
return *this;
}
String &String::operator<<(const String &add)
{ *this += add;
return *this;
}
String &String::operator<<(char add)
{ *this += add;
return *this;
}
c->writeBuffer(Format("%s is awesome", c->getName())); // error POD type
c->writeBuffer(Format("%s is awesome", c->getName().c_str())); // non-error
std::string Format(const std::string &fmt, …)
{
// first we get the size (vsnprintf returns the length of the string
// so we get that first)
va_list args;
va_start(args, fmt.c_str());
int len = vsnprintf(NULL, 0, fmt.c_str(), args);
va_end(args);
// now we create a nice buffer of space to push the string into
std::vector<char>buf;
buf.resize(len+1);
// actually do the formatting into the newly created buffer
va_start(args, fmt.c_str());
vsnprintf(&buf[0], (len+1), fmt.c_str(), args);
va_end(args);
// create our return-string
std::string retStr(&buf[0]);
return retStr;
}
std::string Format(const char *fmt, …)
{
// first we get the size
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
// vsnprintf will return the amount of space required for the string
// so now we create a nice buffer of space to push it into
std::vector<char>buf;
buf.resize(len+1);
// actually do the formatting into the buffer
va_start(args, fmt);
vsnprintf(&buf[0], (len+1), fmt, args);
va_end(args);
// create our return-string
std::string retStr(&buf[0]);
return retStr;
}
namespace Utils {
const string _FormatString( const sint_t narg, const bitset<MAX_BITSET> flags, const string caller, const string fmt, … );
const string __FormatString( const sint_t narg, const bitset<MAX_BITSET> flags, const string caller, const string fmt, va_list val );
#define FormatString( flags, fmt, … ) _FormatString( PP_NARG( __VA_ARGS__ ), flags, _caller, fmt, ##__VA_ARGS__ )
const void _Logger( const sint_t narg, const bitset<MAX_BITSET> flags, const string caller, const string fmt, … );
#define Logger( flags, fmt, … ) _Logger( PP_NARG( __VA_ARGS__ ), flags, _caller, fmt, ##__VA_ARGS__ )
const vector<string> StrTokens( const string input );
template <class T> inline const string toString( const T& t ) { stringstream ss; ss << t; return ss.str(); }
#define toString_( T ) toString( T ).c_str()
};
#define STR(x) #x
#define SX(x) STR(x)
#define _caller __FILE__ ":" SX(__LINE__)
// Thanks to Laurent Deniau @ https://groups.google.com/d/msg/comp.std...
#define PP_NARG(…) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(…) \
PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,…) N
#define PP_RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
const string Utils::_FormatString( const sint_t narg, const bitset<MAX_BITSET> flags, const string caller, const string fmt, … )
{
va_list args;
string output;
va_start( args, fmt );
output = __FormatString( narg, flags, caller, fmt, args );
va_end( args );
return output;
}
const string Utils::__FormatString( const sint_t narg, const bitset<MAX_BITSET> flags, const string caller, const string fmt, va_list val )
{ // Thanks go to Darien @ MudBytes.net for the start of this
va_list args;
vector<string> arguments;
vector<string>::iterator si;
vector<char> buf;
string output, token;
sint_t size = 0;
arguments = StrTokens( fmt );
for ( si = arguments.begin(); si != arguments.end(); si++ )
{
token = *si;
if ( token.find( "%" ) != string::npos ) // not foolproof, but it should catch some worst cases by attempting
size++; // to ensure a matching narg : format specifier count
}
if ( narg != 1 && narg != size ) // if narg == 1 invocation was func( flags, string )
{
Logger( flags, "ERROR: Number of arguments (%ld) did not match number of format specifiers (%ld) at: %s", narg, size, caller.c_str() );
return output = "";
}
va_copy( args, val );
size = vsnprintf( NULL, 0, fmt.c_str(), args );
va_end( args );
va_copy( args, val );
buf.resize( size + 1 );
vsnprintf( &buf[0], ( size + 1 ), fmt.c_str(), args );
va_end( args );
return output = &buf[0];
}
const void Utils::_Logger( const sint_t narg, const bitset<MAX_BITSET> flags, const string caller, const string fmt, … )
{
va_list args;
string output;
va_start( args, fmt );
output = __FormatString( narg, flags, caller, fmt, args );
va_end( args );
if ( output.empty() )
return;
if ( flags.test(UTILS_DEBUG) ) // output caller
clog << current_time_str() << " :: " << output << " [" << caller << "]" << endl;
else
clog << current_time_str() << " :: " << output << endl;
return;
}
const vector<string> Utils::StrTokens( const string input )
{
stringstream ss( input );
istream_iterator<string> si( ss );
istream_iterator<string> end;
vector<string> output( si, end );
return output;
}
Utils::Logger( 0, "test", 5, 10 );
Mon Mar 5 13:26:49 2012 :: ERROR: Number of arguments (2) did not match number of format specifiers (0) at: act_info.c:5185
Utils::Logger( 0, "test %s %s", Utils::toString_( 5 ), Utils::toString_( 10 ) );
Mon Mar 5 13:37:36 2012 :: test 5 10
I spent most of yesterday trying to find something small (not a library) I could work with to add into my own code that is either a type-safe implementation of vsprintf or a "sprintf formater specifier" similar way of working with C++ strings and/or stringstreams. I know strings have format specifiers, but man, that's verbose and clunky. I'd like to ideally retain the format specifiers of sprintf() yet have some kind of type-safety in a custom implementation, which I can't seem to figure out how to do.
Has anyone else attempted/done something similar and could give me some advice on this?
edit: Boost.Format looks like it would be perfect, but, I'd really like to not have external dependencies for anything.