bluecore/engine/Utilities/format.h

920 lines
18 KiB
C
Raw Normal View History

2008-01-16 11:45:17 +00:00
/*
Format a C++ library for typesafe string formating in printf style
(C) 2001 - 2003 by Martin Oberzalek <kingleo@gmx.at>
Examples:
std::cout << format( "Hello %s, I have $05d$ in my pocket", "world", 5 ) << std::endl;
std::cout << format( "Do not try this with printf: %s", 10101 ) << std::endl;
*/
#ifndef format_h
#define format_h
#include <string>
#include <iomanip>
#include <iostream>
/**
Can we use stringstreams or do we have to use the deprecated strstreams instead?
If we have to use strstreams, simple comment the next #define
**/
#define HAVE_STL_SSTREAM
#define NFORMAT
#if __GNUC__ == 2
#undef HAVE_STL_SSTREAM
#endif
#ifdef HAVE_STL_SSTREAM
# include <sstream>
# include <cctype>
# define IS_DIGIT( x ) std::isdigit( x )
#else
extern "C" {
# include <ctype.h>
}
# include <strstream>
# define IS_DIGIT( x ) isdigit( x )
#endif
#ifndef NFORMAT
namespace Format
{
typedef std::string::size_type ST;
class CFormat
{
public:
typedef enum Adjust
{
LEFT,
RIGHT
};
typedef enum Base
{
OCT,
DEC,
HEX
};
typedef enum Floating
{
FIXED,
SCIENTIFIC
};
bool valid;
Adjust adjust;
bool special;
bool sign;
bool grouping; // SUSv2 extension
bool conversion; // glibc 2.2 extension
bool zero;
bool precision_explicit;
bool internal;
Base base;
bool setupper;
Floating floating;
bool showbase;
int width;
int precision;
int strlength;
std::string format;
public:
CFormat() { reset(); }
void set( std::ostream& out );
private:
void reset();
};
/****************************************/
// all the errors that are thrown
// are a cause of a mistake with %* or %*m$
class Error
{
public:
std::string err;
Error( std::string s ) : err( s ) {}
};
/****************************************/
template <class A, class B, class C, class D, class E, class F>
class Format
{
private:
struct Arg
{
bool is_int;
bool is_string;
};
Arg args[6];
std::string format;
A a;
B b;
C c;
D d;
E e;
F f;
unsigned int num_of_args;
std::string s;
public:
Format( const std::string &format, A a, B b, C c, D d, E e, F f, unsigned int num_of_args );
std::string get_string() const { return s; }
private:
void parse();
template <class N> bool is_int( N &n ) { return false; }
bool is_int( int &n ) { return true; }
bool is_int( unsigned int &n ) { return true; }
bool is_int( short &n ) { return true; }
bool is_int( unsigned short ) { return true; }
template <class S> bool is_string( S &s ) { return false; }
bool is_string( std::string& s ) { return true; }
bool is_string( const std::string& s ) { return true; }
bool is_string( char* ) { return true; }
bool is_string( const char* ) { return true; }
int get_int_arg( unsigned int num );
void gen_arg_list();
std::string use_arg( unsigned int i, const CFormat &cf );
template <class S> std::string x2s( S ss, const CFormat &cf )
{
#ifdef HAVE_STL_SSTREAM
std::stringstream str;
str << cf << ss;
std::string st = str.str();
return st;
#else
std::strstream str;
str << cf << ss << std::ends;
std::string st = str.str();
str.freeze(0);
return st;
#endif
}
};
int skip_atoi( std::string s, ST start, ST& pos );
} // namespace Format
inline std::ostream& operator<<( std::ostream& out, Format::CFormat cf )
{
cf.set( out );
return out;
}
template <class A, class B, class C, class D, class E, class F>
Format::Format<A,B,C,D,E,F>::Format( std::string const &format, A a, B b, C c, D d, E e, F f, unsigned int num_of_args )
: format( format ), a(a), b(b), c(c), d(d), e(e), f(f), num_of_args( num_of_args )
{
if( num_of_args > 6 )
throw Error( "Number of args out of range" );
gen_arg_list();
parse();
}
template <class A, class B, class C, class D, class E, class F>
int Format::Format<A,B,C,D,E,F>::get_int_arg( unsigned int num )
{
if( static_cast<unsigned int>(num) > num_of_args - 1 )
throw Error( "The arg you wan't to use is out of range" );
if( num < 0 )
throw Error( "negativ number for arg number not allowed" );
if( args[num].is_int )
{
switch( num )
{
case 0: return *((int*) &a); // I have to cast here cause the compiler
case 1: return *((int*) &b); // will make troubles if any of these
case 2: return *((int*) &c); // values is not an unsigned int.
case 3: return *((int*) &d); // Even if we are sure that
case 4: return *((int*) &e); // an unsigned int value will be returned
case 5: return *((int*) &f);
}
}
else
throw Error( "expecting int arg" );
return 0; // should never be reached
}
template <class A, class B, class C, class D, class E, class F>
void Format::Format<A,B,C,D,E,F>::gen_arg_list()
{
for( unsigned int i = 0; i < num_of_args; i++ )
{
switch( i )
{
case 0:
args[i].is_int = is_int( a );
args[i].is_string = is_string( a );
break;
case 1:
args[i].is_int = is_int( b );
args[i].is_string = is_string( b );
break;
case 2:
args[i].is_int = is_int( c );
args[i].is_string = is_string( c );
break;
case 3:
args[i].is_int = is_int( d );
args[i].is_string = is_string( d );
break;
case 4:
args[i].is_int = is_int( e );
args[i].is_string = is_string( e );
break;
case 5:
args[i].is_int = is_int( f );
args[i].is_string = is_string( f );
break;
}
}
}
template <class A, class B, class C, class D, class E, class F>
std::string Format::Format<A,B,C,D,E,F>::use_arg( unsigned int i, const CFormat &cf )
{
if( i > num_of_args || i < 0 )
throw Error( "out of arg range" );
switch( i )
{
case 0: return x2s( a, cf );
case 1: return x2s( b, cf );
case 2: return x2s( c, cf );
case 3: return x2s( d, cf );
case 4: return x2s( e, cf );
case 5: return x2s( f, cf );
}
return "";
}
template <class A, class B, class C, class D, class E, class F>
void Format::Format<A,B,C,D,E,F>::parse()
{
if( format.empty() )
return;
unsigned int par = 0;
unsigned int use_par = 0;
ST pos = 0;
ST len = format.size();
s = "";
bool had_precision = false;
while( par < num_of_args && pos < len )
{ // while
use_par = par;
if( pos >= len )
break;
if( format[pos] != '%' )
{
s += format[pos];
pos++;
continue;
}
// % digit found
pos++;
if( !(pos < len ) || (format[pos] == '%') )
{
// %% -> %
s += format[pos];
pos++;
continue;
}
// format string found
ST start = pos - 1;
CFormat f;
// process flags
while( (pos < len) )
{
bool finished = false;
switch( format[pos] )
{
case '-' : f.adjust = CFormat::LEFT; break;
case '+' : f.sign = true; break;
case ' ' : f.zero = false; break;
case '#' : f.special = true; break;
case '\'': f.grouping = true; break;
case 'I' : f.conversion = true; break;
case '0' : f.zero = true; break;
default: finished = true; break;
}
if( finished )
break;
pos++;
} // while( (pos < len) )
// get argument number
if( pos < len )
{
// search for the $ digit
unsigned int dp = pos;
while( dp < len && IS_DIGIT( format[dp] ) )
dp++;
if( dp < len && format[dp] == '$' )
{
use_par = skip_atoi( format, pos, pos ) - 1;
pos = dp + 1;
}
}
// get field with
if( pos < len )
{
if( IS_DIGIT( format[pos] ) )
f.width = skip_atoi( format, pos, pos );
else if( format[pos] == '*' )
{
pos++;
// search for the $ digit
unsigned int dp = pos;
while( dp < len && IS_DIGIT( format[dp] ) )
dp++;
if( dp < len && format[dp] == '$' )
{
f.width = get_int_arg( skip_atoi( format, pos, pos ) - 1 );
// skip $ sign
pos++;
}
else
{
f.width = get_int_arg( par );
if( use_par == par )
use_par++;
par++;
}
if( f.width < 0 )
{
f.width *= -1;
f.adjust = CFormat::LEFT;
}
}
}
// precision
if( pos < len )
{
if( format[pos] == '.' )
{
pos++;
if( !(pos < len) )
return;
had_precision = true;
if( IS_DIGIT( format[pos] ) )
f.precision = skip_atoi( format, pos, pos );
else if( format[pos] == '*' )
{
pos++;
// search for the $ digit
unsigned int dp = pos;
while( dp < len && IS_DIGIT( format[dp] ) )
dp++;
if( dp < len && format[dp] == '$' )
{
f.precision = get_int_arg( skip_atoi( format, pos, pos ) - 1 );
// skip $ sign
pos++;
}
else
{
f.precision = get_int_arg( par );
if( use_par == par )
use_par++;
par++;
}
if( f.precision == 0)
f.precision_explicit = true;
if( f.precision < 0 )
f.precision = 0;
}
else
f.precision = 0;
}
}
// lenght modifier
/*
they will be ignored
cause we know the types of the parameter
*/
if( (pos < len) )
{
bool hh = false;
bool ll = false;
bool found = false;
switch( format[pos] )
{
case 'h': hh = true; found = true; break;
case 'l': ll = true; found = true; break;
case 'L':
case 'q':
case 'j':
case 'z':
case 't': found = true; break;
default: break;
}
if(found )
{
pos++;
if( pos < len )
if( hh == true )
{
if( format[pos] == 'h' )
pos++;
}
else if( ll = true )
if( format[pos] == 'l' )
pos++;
}
}
// conversion specifier
if( pos < len )
{
bool invalid = false;
switch( format[pos] )
{
case 'd':
case 'i':
f.base = CFormat::DEC;
if( f.zero && (f.adjust != CFormat::LEFT) )
f.internal = true;
break;
case 'X': f.setupper = true;
case 'x':
f.base = CFormat::HEX;
if( f.special )
f.showbase = true;
break;
case 'o':
f.base = CFormat::OCT;
if( f.special )
f.showbase = true;
break;
case 'E':
f.setupper = true;
case 'e':
if( f.special )
f.sign = true;
f.floating = CFormat::SCIENTIFIC;
break;
case 'F': // not supported
case 'f':
if( f.special )
f.sign = true;
f.floating = CFormat::FIXED;
break;
case 's':
if( f.zero )
f.zero = false;
break;
// unsupported modifiers
case 'G':
case 'g':
case 'A':
case 'a':
case 'c':
case 'C':
case 'S':
case 'P':
case 'n': break;
default: invalid = true;
}
if( !invalid )
f.valid = true;
}
if( f.valid )
{
std::string str;
int upar = par;
if( use_par != par )
upar = use_par;
if( f.base == CFormat::HEX && had_precision && f.special )
{
CFormat f2;
f2.base = f.base;
std::string s = use_arg( upar, f2 );
f.strlength = s.size();
// printf( "str: %s\n", s.c_str() );
}
str = use_arg( upar, f );
// cut string
if( had_precision && args[upar].is_string )
str = str.substr( 0, f.precision );
s += str;
if( use_par == par )
par++;
}
else
{
// copy the invalid format string
for( ST i = start; i<= pos; i++ )
if( i < len )
s += format[i];
}
pos++;
} // while
if( pos < len )
{
while( pos < len )
{
s += format[pos];
pos++;
}
}
}
#else // ifndef NFORMAT
/// converts anything to a string
template<class T>std::string x2s( T what )
{
#ifdef HAVE_STL_SSTREAM
std::stringstream str;
str << what;
std::string s( str.str() );
return s;
#else
std::strstream str;
str << what << std::ends;
std::string s( str.str() );
str.freeze(0);
return s;
#endif
}
#include <cstdio>
namespace Format
{
template <class T> const char* convert( T t ) { return x2s( t ).c_str(); }
#define DEF( TYPE ) \
inline TYPE convert( TYPE t ) { return t; }
DEF( unsigned )
DEF( int )
DEF( char )
DEF( char* )
DEF( const char* )
DEF( short )
DEF( double )
DEF( float )
DEF( long )
#undef DEF
template<class A, class B, class C, class D, class E, class F>
class Format
{
std::string s;
public:
Format( const std::string &format, A a, B b, C c, D d, E e, F f, unsigned int num_of_args )
{
#define D( T ) convert( T )
unsigned buffer_size = 256;
bool cont = false;
do {
cont = false;
char *buffer = new char[buffer_size];
int n = 0;
switch( num_of_args )
{
case 1: n = std::sprintf( buffer, format.c_str(),
D( a ) ); break;
case 2: n = std::sprintf( buffer, format.c_str(),
D( a ), D( b ) ); break;
case 3: n = std::sprintf( buffer, format.c_str(),
D( a ), D( b ), D( c ) ); break;
case 4: n = std::sprintf( buffer, format.c_str(),
D( a ), D( b ), D( c ), D( d ) ); break;
case 5: n = std::sprintf( buffer, format.c_str(),
D( a ), D( b ), D( c ), D( d ), D( e ) ); break;
case 6: n = std::sprintf( buffer, format.c_str(),
D( a ), D( b ), D( c ), D( d ), D( e ), D( f ) ); break;
}
if( (unsigned) n >= buffer_size - 2 )
{
buffer_size *= 2;
cont = true;
n = 0;
}
for( int i = 0; i < n; i++ )
s += buffer[i];
delete[] buffer;
} while( cont );
#undef D
}
std::string get_string() const { return s; }
};
}
#endif
template <class A, class B, class C, class D, class E, class F>
inline std::string format( std::string fs, A a, B b, C c, D d, E e, F f )
{
return Format::Format<A,B,C,D,E,F>( fs, a, b, c, d, e, f, 6).get_string();
}
template <class A, class B, class C, class D, class E>
inline std::string format( std::string fs, A a, B b, C c, D d, E e )
{
return Format::Format<A,B,C,D,E,char>( fs, a, b, c, d, e, 0, 5).get_string();
}
template <class A, class B, class C, class D>
inline std::string format( std::string fs, A a, B b, C c, D d)
{
return Format::Format<A,B,C,D,char,char>( fs, a, b, c, d, 0, 0, 4).get_string();
}
template <class A, class B, class C>
inline std::string format( std::string fs, A a, B b, C c )
{
return Format::Format<A,B,C,char,char,char>( fs, a, b, c, 0, 0, 0, 3).get_string();
}
template <class A, class B>
inline std::string format( std::string fs, A a, B b )
{
return Format::Format<A,B,char,char,char,char>( fs, a, b, 0, 0, 0, 0, 2).get_string();
}
template <class A>
inline std::string format( std::string fs, A a)
{
return Format::Format<A,char,char,char,char,char>( fs, a, 0, 0, 0, 0, 0, 1).get_string();
}
namespace Format
{
template<typename ostream> class PrintF
{
private:
int level;
int dlevel;
int dmodule;
int module;
public:
ostream &out;
public:
PrintF( ostream &out = std::cout, int module = -1, int debug_level = -1 )
: level( debug_level ), dlevel( debug_level ),
dmodule( module), module( module), out( out )
{}
void set_debug_level( int dlevel_ ) { dlevel = dlevel_; }
void set_module( int module_ ) { dmodule = module_; }
PrintF operator()( int module_ )
{
PrintF printf( *this );
printf.module = module_;
return printf;
}
PrintF operator[]( int level_ )
{
PrintF printf( *this );
printf.level = level_;
return printf;
}
template<typename T> PrintF& operator<<( const T &t )
{
if( check() )
out << t;
return *this;
}
// io manipulator overloading
PrintF& operator<<(ostream& (*f)(ostream&))
{
if( check() )
out << f;
return *this;
}
PrintF& operator()( std::string fs )
{
if( check() )
out << fs;
return *this;
}
template<typename A> PrintF& operator()( std::string fs, const A &a )
{
if( check() )
out << format( fs, a );
return *this;
}
template<typename A, typename B>
PrintF& operator()( std::string fs, const A &a, const B &b )
{
if( check() )
out << format( fs, a, b );
return *this;
}
template<typename A, typename B, typename C>
PrintF& operator()( std::string fs, const A &a, const B &b, const C &c )
{
if( check() )
out << format( fs, a, b, c );
return *this;
}
template<typename A, typename B, typename C, typename D>
PrintF& operator()( std::string fs, const A &a, const B &b, const C &c, const D &d )
{
if( check() )
out << format( fs, a, b, c, d );
return *this;
}
template<typename A, typename B, typename C, typename D, typename E>
PrintF& operator()( std::string fs, const A &a, const B &b, const C &c, const D &d, const E &e )
{
if( check() )
out << format( fs, a, b, c, d, e );
return *this;
}
template<typename A, typename B, typename C, typename D, typename E, typename F>
PrintF& operator()( std::string fs, const A &a, const B &b, const C &c, const D &d, const E &e, const F &f )
{
if( check() )
out << format( fs, a, b, c, d, e, f );
return *this;
}
bool check( int module, int level ) const
{
if( module == dmodule || dmodule == -1 )
{
if( dlevel == -1 )
return true;
if( level <= dlevel )
return true;
}
return false;
}
private:
bool check() const { return check( module, level ); }
};
}
#undef IS_DIGIT
#endif