/* */ #ifndef _LIBCXXEXTRAS_PRINTF_ #define _LIBCXXEXTRAS_PRINTF_ #include #include #include #include namespace cxxextras { class cprintf_toomanyargs: public ::std::exception { }; class cprintf_toofewargs: public ::std::exception { }; class cprintf_badformat: public ::std::exception { const char *m_reason; public: cprintf_badformat(const char *reason): m_reason(reason) { } const char* what() const noexcept override { return m_reason; } }; namespace _bits { enum e_cprintf_type { TYPE_AUTO, TYPE_BOOLEAN, TYPE_BINARY, TYPE_OCT, TYPE_INT, TYPE_INTU, TYPE_INTS, TYPE_HEXLC, TYPE_HEXUC, TYPE_STRING, }; struct PrintfFlags { unsigned int width; unsigned int precision; unsigned int flags; struct Left {}; constexpr PrintfFlags(PrintfFlags x, Left _): width(x.width), precision(x.precision), flags(x.flags | 1) {} struct Sign {}; constexpr PrintfFlags(PrintfFlags x, Sign _): width(x.width), precision(x.precision), flags(x.flags | 2) {} struct Space {}; constexpr PrintfFlags(PrintfFlags x, Space _): width(x.width), precision(x.precision), flags(x.flags | 4) {} struct Alt {}; constexpr PrintfFlags(PrintfFlags x, Alt _): width(x.width), precision(x.precision), flags(x.flags | 8) {} struct Zero {}; constexpr PrintfFlags(PrintfFlags x, Zero _): width(x.width), precision(x.precision), flags(x.flags | 16) {} struct ArgWidth {}; constexpr PrintfFlags(PrintfFlags x, ArgWidth _, unsigned int v): width(v), precision(x.precision), flags(x.flags) {} struct ArgPrec {}; constexpr PrintfFlags(PrintfFlags x, ArgPrec _, unsigned int v): width(x.width), precision(v), flags(x.flags) {} struct FAuto {}; constexpr PrintfFlags(PrintfFlags x, FAuto _): width(x.width), precision(x.precision), flags(x.flags | 0x000) {} struct FString {}; constexpr PrintfFlags(PrintfFlags x, FString _): width(x.width), precision(x.precision), flags(x.flags | 0x100) {} struct FBool {}; constexpr PrintfFlags(PrintfFlags x, FBool _): width(x.width), precision(x.precision), flags(x.flags | 0x200) {} struct FBinary {}; constexpr PrintfFlags(PrintfFlags x, FBinary _): width(x.width), precision(x.precision), flags(x.flags | 0x300) {} struct FOct {}; constexpr PrintfFlags(PrintfFlags x, FOct _): width(x.width), precision(x.precision), flags(x.flags | 0x400) {} struct FUDec {}; constexpr PrintfFlags(PrintfFlags x, FUDec _): width(x.width), precision(x.precision), flags(x.flags | 0x500) {} struct FSDec {}; constexpr PrintfFlags(PrintfFlags x, FSDec _): width(x.width), precision(x.precision), flags(x.flags | 0x600) {} struct FHexL {}; constexpr PrintfFlags(PrintfFlags x, FHexL _): width(x.width), precision(x.precision), flags(x.flags | 0x700) {} struct FHexU {}; constexpr PrintfFlags(PrintfFlags x, FHexU _): width(x.width), precision(x.precision), flags(x.flags | 0x800) {} constexpr PrintfFlags(): width(0), precision(0), flags(0) { } }; struct s_cprintf_fmt { bool isValid = false; unsigned int precision = 0; unsigned int minLength = 0; bool padLeft = false; bool showSign = false; bool showSpace = false; bool altFormat = false; bool padZero = false; enum e_cprintf_type type; }; ; constexpr bool isdigit_s(const char ch) { return '0' <= ch && ch <= '9'; } constexpr unsigned todigit(const char ch) { return ch - '0'; } }; // namespace _bits typedef ::std::function cprintf_cb; template size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags &fmt, Arg arg); template constexpr bool cprintf_val_chk(const _bits::PrintfFlags fmt, Arg arg); constexpr bool cprintf_val_chk(const _bits::PrintfFlags fmt, const char* arg) { return true; } size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, const char* arg) { unsigned int len; for(len = 0; arg[len]; len ++) ; puts(arg, len); return len; } constexpr bool cprintf_val_chk(const _bits::PrintfFlags& fmt, int arg) { return true; } size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, int arg) { size_t len = ::std::snprintf(nullptr, 0, "%i", arg); char buf[len+1]; ::std::snprintf(buf, len+1, "%i", arg); puts(buf, len); return len; } constexpr bool cprintf_val_chk(const _bits::PrintfFlags& fmt, unsigned int arg) { return true; } size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, unsigned int arg) { size_t len = ::std::snprintf(nullptr, 0, "%u", arg); char buf[len+1]; ::std::snprintf(buf, len+1, "%u", arg); puts(buf, len); return len; } namespace _bits { namespace _printf { template class _str { const char m_buf[N]; unsigned m_ofs; public: constexpr _str(const char buf[N]): m_buf(buf), m_ofs(0) { } constexpr _str(const char buf[N], unsigned ofs): m_buf(buf), m_ofs(ofs) { } constexpr _str operator+(const unsigned o) { return _str(m_buf, m_ofs+o); } constexpr char operator*() { return m_buf[m_ofs]; } }; template constexpr bool val(const char* fmt, Args... args); template constexpr bool val_fmt_done(const char * fmt, PrintfFlags item, Fmt fmtcode) { return false ? false : throw "Too few arguments"; } template constexpr bool val_fmt_done(const char* fmt, PrintfFlags item, Fmt fmtcode, Arg arg, Args... args) { return cprintf_val_chk(item, arg) && val(fmt+1, args...); } // --- Format code template constexpr bool val_fmt_fmt(const char * fmt, PrintfFlags item, Args... args) { return *fmt == '\0' ? throw "ERROR: NUL byte in format specifier" : *fmt == '?' ? val_fmt_done(fmt, item, PrintfFlags::FAuto(), args...) : *fmt == 's' ? val_fmt_done(fmt, item, PrintfFlags::FString(),args...) : *fmt == 'B' ? val_fmt_done(fmt, item, PrintfFlags::FBool(), args...) : *fmt == 'b' ? val_fmt_done(fmt, item, PrintfFlags::FBinary(),args...) : *fmt == 'o' ? val_fmt_done(fmt, item, PrintfFlags::FOct(), args...) : *fmt == 'i' ? val_fmt_done(fmt, item, PrintfFlags::FSDec(), args...) : *fmt == 'u' ? val_fmt_done(fmt, item, PrintfFlags::FUDec(), args...) : *fmt == 'x' ? val_fmt_done(fmt, item, PrintfFlags::FHexL(), args...) : *fmt == 'X' ? val_fmt_done(fmt, item, PrintfFlags::FHexU(), args...) : throw cprintf_badformat("Unknown character in format string"); } // --- Size modifier (not implemented, not needed?) template constexpr bool val_fmt_size(const char * fmt, PrintfFlags item, Args... args) { // TODO: Size characters? return val_fmt_fmt(fmt, item, args...); } // --- Precision template constexpr bool val_fmt_prec_val(const char * fmt, unsigned int value, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? val_fmt_prec_val(fmt+1, value*10+_bits::todigit(*fmt), item, args...) : val_fmt_size(fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), value), args...); } template constexpr bool val_fmt_prec_arg(const char *fmt, PrintfFlags item, unsigned int size, Args... args) { return val_fmt_prec_opt(fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), size), args...); } template constexpr bool val_fmt_prec_arg(const char *fmt, PrintfFlags item, Arg size, Args... args) { //static_assert(false, "Invalid type for precision modifier, must be 'unsigned int'"); return false ? false : throw "Invalid type for precision modifier, must be 'unsigned int'"; } constexpr bool val_fmt_prec_arg(const char *fmt, PrintfFlags item) { //static_assert(false, "Too few arguments when getting precision modifier"); return false ? false : throw "Too few arguments when getting precision modifier"; } template constexpr bool val_fmt_prec(const char * fmt, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? val_fmt_prec_val(fmt+1, _bits::todigit(*fmt), item, args...) : *fmt == '*' ? val_fmt_prec_arg(fmt+1, item, args...) : val_fmt_size(fmt, item, args...); } template constexpr bool val_fmt_prec_opt(const char * fmt, PrintfFlags item, Args... args) { return *fmt == '.' ? val_fmt_prec(fmt+1, item, args...) : val_fmt_size(fmt, item, args...); } // --- Field Width --- template constexpr bool val_fmt_width_val(const char* fmt, unsigned int size, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? val_fmt_width_val(fmt+1, size*10+_bits::todigit(*fmt), item, args...) : val_fmt_prec_opt(fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), size), args...); } template constexpr bool val_fmt_width_arg(const char *fmt, PrintfFlags item, unsigned int size, Args... args) { return val_fmt_prec_opt(fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), size), args...); } template constexpr bool val_fmt_width_arg(const char *fmt, PrintfFlags item, Arg size, Args... args) { //static_assert(false, "Invalid type for width modifier, must be 'unsigned int'"); return false ? false : throw "Invalid type for width modifier, must be 'unsigned int'"; } constexpr bool val_fmt_width_arg(const char *fmt, PrintfFlags item) { //static_assert(false, "Too few arguments when reading width for width modifier"); return false ? false : throw "Too few arguments when reading width for width modifier"; } template constexpr bool val_fmt_width(const char * fmt, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? val_fmt_width_val(fmt+1, _bits::todigit(*fmt), item, args...) : *fmt == '*' ? val_fmt_width_arg(fmt+1, item, args...) : val_fmt_prec_opt(fmt, item, args...); } // --- Flags template constexpr bool val_fmt_flags(const char * fmt, PrintfFlags item, Args... args) { return *fmt == '-' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Left()) , args...) : *fmt == '+' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Sign()) , args...) : *fmt == ' ' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Space()), args...) : *fmt == '#' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Alt()) , args...) : *fmt == '0' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Zero()) , args...) : val_fmt_width(fmt, item, args...); } // --- Literal '%' template constexpr bool val_fmt_start(const char* fmt, Args... args) { return *fmt == '%' ? val(fmt+1, args...) : val_fmt_flags(fmt, PrintfFlags(), args...); } template constexpr bool val(const char* fmt, Args... args) { return *fmt == '\0' ? true : *fmt == '%' ? val_fmt_start(fmt+1, args...) : val(fmt+1, args...); } size_t run(cprintf_cb puts, const char *fmt); template size_t run(cprintf_cb puts, const char * fmt, Args... args); // --- Print formatted value template constexpr size_t run_fmt_done(cprintf_cb puts, const char * fmt, PrintfFlags item, Fmt fmtcode) { throw cprintf_toofewargs(); } template size_t run_fmt_done(cprintf_cb puts, const char * fmt, PrintfFlags item, Fmt fmtcode, Arg val, Args... args) { if( !puts ) return run(puts, fmt+1, args...); else return ::cxxextras::cprintf_val(puts, PrintfFlags(item, fmtcode), val) + run(puts, fmt+1, args...); } // --- Format code template size_t run_fmt_fmt(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args) { return *fmt == '\0' ? throw "ERROR: NUL byte in format specifier" : *fmt == '?' ? run_fmt_done(puts, fmt, item, PrintfFlags::FAuto(), args...) : *fmt == 's' ? run_fmt_done(puts, fmt, item, PrintfFlags::FString(),args...) : *fmt == 'B' ? run_fmt_done(puts, fmt, item, PrintfFlags::FBool(), args...) : *fmt == 'b' ? run_fmt_done(puts, fmt, item, PrintfFlags::FBinary(),args...) : *fmt == 'o' ? run_fmt_done(puts, fmt, item, PrintfFlags::FOct(), args...) : *fmt == 'i' ? run_fmt_done(puts, fmt, item, PrintfFlags::FSDec(), args...) : *fmt == 'u' ? run_fmt_done(puts, fmt, item, PrintfFlags::FUDec(), args...) : *fmt == 'x' ? run_fmt_done(puts, fmt, item, PrintfFlags::FHexL(), args...) : *fmt == 'X' ? run_fmt_done(puts, fmt, item, PrintfFlags::FHexU(), args...) : throw cprintf_badformat("Unknown character in format string"); } // --- Size modifier (not implemented, not needed?) template size_t run_fmt_size(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args) { // TODO: Size characters? return run_fmt_fmt(puts, fmt, item, args...); } // --- Precision template size_t run_fmt_prec_val(cprintf_cb puts, const char * fmt, unsigned int val, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? run_fmt_prec_val(puts, fmt+1, val*10+_bits::todigit(*fmt), item, args...) : run_fmt_size(puts, fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), val), args...); } template size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, unsigned int size, Args... args) { return run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), size), args...); } template size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, Arg size, Args... args) { throw cprintf_badformat("Invalid type for printf precision modifier"); } size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item) { throw cprintf_toofewargs(); } template size_t run_fmt_prec(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? run_fmt_prec_val(puts, fmt+1, _bits::todigit(*fmt), item, args...) : *fmt == '*' ? run_fmt_prec_arg(puts, fmt+1, item, args...) : run_fmt_size(puts, fmt, item, args...); } template size_t run_fmt_prec_opt(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args) { return *fmt == '.' ? run_fmt_prec(puts, fmt+1, item, args...) : run_fmt_size(puts, fmt, item, args...); } // --- Field Width --- template size_t run_fmt_width_val(cprintf_cb puts, const char* fmt, unsigned int val, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? run_fmt_width_val(puts, fmt+1, val*10+_bits::todigit(*fmt), item, args...) : run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), val), args...); } template size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, unsigned int size, Args... args) { return run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), size), args...); } template size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, Arg size, Args... args) { throw cprintf_badformat("Invalid type for printf width modifier"); } size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item) { throw cprintf_toofewargs(); } template size_t run_fmt_width(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args) { return _bits::isdigit_s(*fmt) ? run_fmt_width_val(puts, fmt+1, _bits::todigit(*fmt), item, args...) : *fmt == '*' ? run_fmt_width_arg(puts, fmt+1, item, args...) : run_fmt_prec_opt(puts, fmt, item, args...); } // --- Flags template size_t run_fmt_flags(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args) { return *fmt == '-' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Left()) , args...) : *fmt == '+' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Sign()) , args...) : *fmt == ' ' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Space()), args...) : *fmt == '#' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Alt()) , args...) : *fmt == '0' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Zero()) , args...) : run_fmt_width(puts, fmt, item, args...); } // --- Literal '%' template size_t run_fmt_start(cprintf_cb puts, const char * fmt, Args... args) { return *fmt == '%' ? (puts("%", 1), 1 + run(puts, fmt+1, args...)) : run_fmt_flags(puts, fmt, PrintfFlags(), args...); } // --- Root template size_t run(cprintf_cb puts, const char * fmt, Args... args) { int ofs; for( ofs = 0; fmt[ofs] != '\0' && fmt[ofs] != '%'; ofs ++ ) ; if( fmt[ofs] != '%' ) throw cprintf_toomanyargs(); if(ofs > 0 && puts) puts(fmt, ofs); return ofs + run_fmt_start(puts, fmt+ofs+1, args...); } size_t run(cprintf_cb puts, const char *fmt) { int ofs; for( ofs = 0; fmt[ofs] != '\0' && fmt[ofs] != '%'; ofs ++ ) ; if(ofs > 0 && puts) puts(fmt, ofs); return ofs + (fmt[ofs] == '%' ? run_fmt_start(puts, fmt+ofs+1) : 0); } } }; // namespace _bits #define cprintf(puts, fmt, ...) static_assert(::cxxextras::_bits::_printf::val(fmt, __VA_ARGS__),"");::cxxextras::_bits::_printf::run(puts, fmt, __VA_ARGS__) //template //size_t cprintf(cprintf_cb puts, const char *fmt, Args... args) //{ // _bits::_printf::run(nullptr, fmt, args...); // return _bits::_printf::run(puts, fmt, args...); //} }; #endif // vim: ft=cpp