#include "printf.hh" #include #include #include static const char DigitBufUp[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; static const char DigitBufLo[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; template void PrintfFormatter::MakeFrom(const std::basic_string& format) { for(std::size_t b = format.size(), a = 0; a < b; ) { CT c = format[a]; if(c == '%') { std::size_t percent_begin = a++; arg argument; if(a < format.size() && format[a] == '-') { argument.leftalign = true; ++a; } if(a < format.size() && format[a] == '+') { argument.sign = true; ++a; } if(a < format.size() && format[a] == '0') { argument.zeropad = true; ++a; } if(a < format.size() && format[a] == '*') { argument.param_minwidth = true; ++a; } else while(a < format.size() && (format[a] >= '0' && format[a] <= '9')) argument.min_width = argument.min_width*10 + (format[a++] - '0'); if(a < format.size() && format[a] == '.') { argument.max_width = 0; if(++a < format.size() && format[a] == '*') { argument.param_maxwidth = true; ++a; } else while(a < format.size() && (format[a] >= '0' && format[a] <= '9')) argument.max_width = argument.max_width*10 + (format[a++] - '0'); } another_formatchar: if(a >= format.size()) goto invalid_format; switch(format[a++]) { case 'z': case 'l': goto another_formatchar; // ignore 'l' or 'z' case 'S': case 's': argument.format = arg::as_string; break; case 'C': case 'c': argument.format = arg::as_char; break; case 'x': argument.base = arg::hex; argument.format = arg::as_int; break; case 'X': argument.base = arg::hexup; argument.format = arg::as_int; break; case 'o': argument.base = arg::oct; argument.format = arg::as_int; break; case 'b': argument.base = arg::bin; argument.format = arg::as_int; break; case 'i': case 'u': case 'd': argument.format = arg::as_int; break; // f: TODO: decimal notation. Precision is assumed as 6 if not specified. // e: TODO: exponential notation. Precision is assumed as 6 if not specified. // g: TODO: Uses 'e' if exponent <= -4 or exponent >= precision. case 'g': case 'e': case 'f': argument.format = arg::as_float; break; default: invalid_format: fprintf(stderr, "Invalid format...\n"); a = percent_begin + 1; trail += '%'; continue; } argument.before.swap(trail); formats.push_back(argument); } else { trail += c; ++a; } } } // When no parameters are remaining void PrintfFormatter::Execute(PrintfFormatter::State& state) { for(std::size_t pos = (state.position + 3) / 4; pos < formats.size(); ++pos) { state.result.append(formats[pos].before); state.result.append( (std::size_t) formats[pos].min_width, L' ' ); } state.result += trail; formats.clear(); state.position = 0; trail.clear(); } namespace { template::value> struct IsNegative { static bool Test(T value) { return value < 0; } }; template struct IsNegative { static bool Test(T) { return false; } }; template::value, bool IsFloat = std::is_floating_point::value> class Categorize { };//: public std::integral_constant { }; // 1 = integers template class Categorize: public std::integral_constant { }; // 2 = floats template class Categorize: public std::integral_constant { }; // 3 = character pointers template<> class Categorize: public std::integral_constant { }; template<> class Categorize: public std::integral_constant { }; template<> class Categorize: public std::integral_constant { }; template<> class Categorize: public std::integral_constant { }; template<> class Categorize: public std::integral_constant { }; template<> class Categorize: public std::integral_constant { }; // 4 = strings template class Categorize,false,false>: public std::integral_constant { }; template::type>::type >::value> struct PrintfFormatDo { }; template struct PrintfFormatDo // ints { static void Do(PrintfFormatter::argsmall& arg, std::basic_string & result, T part); static T IntValue(T part) { return part; } }; template struct PrintfFormatDo // floats { static void Do(PrintfFormatter::argsmall& arg, std::basic_string & result, T part); static long long IntValue(T part) { return part; } }; template struct PrintfFormatDo // char-pointers { typedef typename std::remove_cv< typename std::remove_reference< decltype(*(T())) >::type>::type ctype; static void Do(PrintfFormatter::argsmall& arg, std::basic_string & result, T part); static void DoString(PrintfFormatter::argsmall& arg, std::basic_string & result, T part, std::size_t length); static long long IntValue(T part); }; template struct PrintfFormatDo // strings { using ctype = typename T::value_type; static void Do(PrintfFormatter::argsmall& arg, std::basic_string & result, const T& part); static long long IntValue(const T& part); }; template void PrintfFormatDo::Do(PrintfFormatter::argsmall& arg, std::basic_string & result, T part) { // "part" is an integer type switch(arg.format) { case PrintfFormatter::argsmall::as_char: { // Interpret as character char32_t n = part; PrintfFormatDo::DoString(arg, result, &n, 1); break; } case PrintfFormatter::argsmall::as_string: case PrintfFormatter::argsmall::as_int: case PrintfFormatter::argsmall::as_float: std::string s; // Use this contrived expression rather than "part < 0" // to avoid a compiler warning about expression being always false // due to limited datatype (when instantiated for unsigned types) if(IsNegative::Test(part)) { s += '-'; part = -part; } else if(arg.sign) s += '+'; std::string digitbuf; const char* digits = (arg.base & 64) ? DigitBufUp : DigitBufLo; int base = arg.base & ~64; while(part != 0) { digitbuf += digits[ part % base ]; part /= base; } // Append the digits in reverse order for(std::size_t a = digitbuf.size(); a--; ) s += digitbuf[a]; if(digitbuf.empty()) s += '0'; // Delegate the width-formatting arg.max_width = ~0u; PrintfFormatDo::DoString(arg, result, s.data(), s.size()); break; } } template void PrintfFormatDo::Do(PrintfFormatter::argsmall& arg, std::basic_string & result, T part) { // "part" is a float switch(arg.format) { case PrintfFormatter::argsmall::as_char: { // Cast into integer, and interpret as character char32_t n = part; PrintfFormatDo::DoString(arg, result, &n, 1); break; } case PrintfFormatter::argsmall::as_int: // Cast into integer, and interpret PrintfFormatDo::Do( arg, result, (long long)(part) ); break; case PrintfFormatter::argsmall::as_string: case PrintfFormatter::argsmall::as_float: // Print float // TODO: Handle different formatting styles (exponents, automatic precisions etc.) // better than this. std::stringstream s; if(arg.sign) s << std::showpos; if(arg.base == PrintfFormatter::arg::hex) s << std::setbase(16); if(arg.base == PrintfFormatter::arg::oct) s << std::setbase(8); if(arg.base == PrintfFormatter::arg::bin) s << std::setbase(2); s << std::setprecision( arg.max_width); s << std::fixed; s << part; arg.max_width = ~0u; std::string s2 = s.str(); PrintfFormatDo::DoString(arg, result, s2.data(), s2.size()); break; } } template void PrintfFormatDo::Do(PrintfFormatter::argsmall& arg, std::basic_string & result, T part) { // "part" is a character pointer std::size_t length = 0; for(const ctype* p = part; *p; ++p) ++length; switch(arg.format) { case PrintfFormatter::argsmall::as_char: case PrintfFormatter::argsmall::as_string: // Do with a common function for char* and basic_string DoString(arg, result, part, length); break; case PrintfFormatter::argsmall::as_int: case PrintfFormatter::argsmall::as_float: // Cast string into integer! // Delegate it... PrintfFormatDo>::Do( arg, result, std::basic_string(part, length) ); break; } } template void PrintfFormatDo::DoString( PrintfFormatter::argsmall& arg, std::basic_string & result, T part, std::size_t length) { // Character pointer version if(length > arg.max_width) length = arg.max_width; result.reserve( result.size() + (length < arg.min_width ? arg.min_width : length) ); char32_t pad = arg.zeropad ? L'0' : L' '; if(length < arg.min_width && !arg.leftalign) result.append( std::size_t(arg.min_width-length), pad ); for( std::size_t a=0; a long long PrintfFormatDo::IntValue(T part) { // Delegate it return PrintfFormatDo>::IntValue( std::basic_string(part) ); } template void PrintfFormatDo::Do(PrintfFormatter::argsmall& arg, std::basic_string & result, const T& part) { // "part" is a string switch(arg.format) { case PrintfFormatter::argsmall::as_char: case PrintfFormatter::argsmall::as_string: // Do with a common function for char* and basic_string PrintfFormatDo::DoString(arg, result, part.data(), part.size() ); break; case PrintfFormatter::argsmall::as_int: // Cast string into integer! PrintfFormatDo::Do(arg, result, IntValue(part)); break; case PrintfFormatter::argsmall::as_float:; // Cast string into float! std::basic_stringstream s; double d = 0.; s << part; s >> d; // Cannot use std::stod(), because it is only // defined for basic_string. PrintfFormatDo::Do(arg, result, d); break; } } template long long PrintfFormatDo::IntValue(const T& part) { // Delegate it std::basic_stringstream s; long long l = 0; s << part; s >> l; return l; //return std::stoll(part); // Doesn't work with char32_t } } template void PrintfFormatter::ExecutePart(PrintfFormatter::State& state, T part) { std::size_t position = state.position, pos = position / 4, subpos = position % 4; if(pos >= formats.size()) return; using TT = typename std::remove_cv< typename std::remove_reference::type>::type; if(subpos == 0) { state.result += formats[pos].before; state.minwidth = formats[pos].min_width; state.maxwidth = formats[pos].max_width; state.leftalign = formats[pos].leftalign; } if(subpos == 0) { if(formats[pos].param_minwidth) { // This param should be an integer. auto p = PrintfFormatDo::IntValue(part); // Use this contrived expression rather than "p < 0" // to avoid a compiler warning about expression being always false // due to limited datatype (when instantiated for unsigned types) if(IsNegative::Test(p)) { state.leftalign = true; state.minwidth = -p; } else state.minwidth = p; state.position = pos*4 + 1; return; } goto pos1; } if(subpos == 1) pos1: { if(formats[pos].param_maxwidth) { // This param should be an integer. state.maxwidth = PrintfFormatDo::IntValue(part); state.position = pos*4 + 2; return; } //goto pos2; } //if(subpos == 2) pos2: //{ argsmall a { state.minwidth, state.maxwidth, state.leftalign, formats[pos].sign, formats[pos].zeropad, formats[pos].base, formats[pos].format }; PrintfFormatDo::Do(a, state.result, part); state.position = (pos+1)*4; //} } template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, char); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, char16_t); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, char32_t); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, short); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, int); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, long); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, long long); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, unsigned char); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, unsigned short); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, unsigned int); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, unsigned long); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, unsigned long long); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, bool); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, float); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, double); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, const char*); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, const std::string&); template void PrintfFormatter::ExecutePart(PrintfFormatter::State&, const std::basic_string&); template void PrintfFormatter::MakeFrom(const std::basic_string&); template void PrintfFormatter::MakeFrom(const std::basic_string&); PrintfProxy operator ""_f(const char* format, std::size_t num) { PrintfProxy p; p.data->first.MakeFrom( std::basic_string{format,num} ); return p; } PrintfProxy PrintfProxy::operator+ (PrintfProxy&& b) && { // Finish our string data->first.Execute(data->second); // Finish their string b.data->first.Execute(b.data->second); // Append their result to us data->second.result += b.data->second.result; return std::move(*this); } PrintfProxy PrintfProxy::operator %(PrintfProxy&& arg) && { // Finish their string arg.data->first.Execute(arg.data->second); // Execute their outcome as a parameter to our printf data->first.ExecutePart&> (data->second, arg.data->second.result); return std::move(*this); }