#ifndef __CXXMPH_STRING_UTIL_H__ #define __CXXMPH_STRING_UTIL_H__ // Helper functions for string formatting and terminal output. Should be used // only for debugging and tests, since performance was not a concern. // Implemented using variadic templates because it is cool. // // Adds the extra format %v to the printf formatting language. Uses the method // cxxmph::tostr to implement custom printers and fallback to operator // ostream::operator<< otherwise. #include #include #include #include #include #include #include #include #define CXXMPH_DEBUGLN(fmt) variadic_print(__FILE__, __LINE__, &std::cerr, fmt) #define CXXMPH_INFOLN(fmt) variadic_print(__FILE__, __LINE__, &std::cout, fmt) namespace cxxmph { using std::pair; using std::string; using std::ostream; using std::vector; template void tostr(ostream *out, const T& v) { *out << v; } inline void tostr(std::ostream* out, uint8_t v) { *out << static_cast(v); } template inline void tostr(ostream* out, const vector& v) { *out << "["; for (uint32_t i = 0; i < v.size(); ++i) { tostr(out, v[1]); if (i != v.size() - 1)*out << " "; } *out << "]"; } template inline void tostr(ostream* out, const pair& v) { *out << "("; tostr(out, v.first); *out << ","; tostr(out, v.second); *out << ")"; } template std::string infoln(const std::string& format_string, Args&&... args) { return stream_printf(format_string, 0, &std::cout, std::forward(args)...); } bool stream_printf( const std::string& format_string, uint32_t offset, std::ostream* out); template struct pod_snprintf {}; template <> struct pod_snprintf { template int operator()(char*, size_t, const char*, const T&) { return -1; } }; template <> struct pod_snprintf { template int operator()(char* str, size_t size, const char* format, const T& v) { return snprintf(str, size, format, v); } }; template bool stream_printf(const std::string& format_string, uint32_t offset, std::ostream* out, const T& value, Args&&... args) { auto b = format_string.c_str() + offset; auto txt = format_string.c_str() + offset; for (; *txt; ++txt) { if (*txt == '%') { if (*(txt + 1) != '%') break; *out << "%"; return stream_printf(format_string, offset + 2, out, value, std::forward(args)...); } } if (txt != b) { *out << string(b, txt - b); } auto fmt = txt + 1; while (*fmt && *fmt != '%') ++fmt; if (strncmp(txt, "%v", 2) == 0) { txt += 2; tostr(out, value); if (txt != fmt) *out << string(txt, fmt); } else { char buf[256]; // Is this enough? auto n = pod_snprintf::value>()( buf, 256, std::string(txt, fmt).c_str(), value); if (n < 0) return false; *out << buf; } return stream_printf(format_string, fmt - format_string.c_str(), out, std::forward(args)...); } template std::string format(const std::string& format_string, Args&&... args) { std::ostringstream out; if (!stream_printf(format_string, 0, &out, std::forward(args)...)) { return std::string(); }; return out.str(); } struct variadic_print { variadic_print(const std::string& file, uint32_t line, std::ostream* out, const std::string& format_line) : file_(file), line_(line), out_(out), format_line_(format_line) {} template void operator()(Args&&... args) { std::string fancy_format = "%s:%d: "; fancy_format += format_line_; stream_printf(fancy_format, 0, out_, std::forward(args)...); } const std::string& file_; const uint32_t& line_; std::ostream* out_; const std::string& format_line_; }; } // namespace cxxmph #endif // __CXXMPH_STRING_UTIL_H__