#pragma once #include #include #include // TODO : make single functions that manages stringstreams and ostreams namespace Scop { namespace Internal { template void Format(std::stringstream& ss, It first, It last) { for(auto it = first; it != last; ++it) { switch(*it) { case '%': throw std::invalid_argument{"too few arguments"}; case '/': ++it; if(it == last) throw std::invalid_argument{"stray '/'"}; [[fallthrough]]; default: ss << *it; } } } template void Format(std::stringstream& ss, It first, It last, const T& arg, const Args&... args) { for(auto it = first; it != last; ++it) { switch(*it) { case '%': ss << arg; return Format(ss, ++it, last, args...); case '/': ++it; if(it == last) throw std::invalid_argument{"stray '/'"}; [[fallthrough]]; default: ss << *it; } } throw std::invalid_argument{"too many arguments"}; } template void Format(std::ostream& os, It first, It last) { for(auto it = first; it != last; ++it) { switch(*it) { case '%': throw std::invalid_argument{"too few arguments"}; case '/': ++it; if(it == last) throw std::invalid_argument{"stray '/'"}; [[fallthrough]]; default: os << *it; } } } template void Format(std::ostream& os, It first, It last, const T& arg, const Args&... args) { for(auto it = first; it != last; ++it) { switch(*it) { case '%': os << arg; return Format(os, ++it, last, args...); case '/': ++it; if(it == last) throw std::invalid_argument{"stray '/'"}; [[fallthrough]]; default: os << *it; } } throw std::invalid_argument{"too many arguments"}; } template struct Formatter { std::string_view format; std::tuple args; }; template void FormatHelper(std::stringstream& ss, const Formatter& formatter, std::index_sequence) { Format(ss, formatter.format.begin(), formatter.format.end(), std::get(formatter.args)...); } template std::stringstream& operator<<(std::stringstream& ss, const Formatter& printer) { FormatHelper(ss, printer, std::index_sequence_for{}); return ss; } template void FormatHelper(std::ostream& os, const Formatter& formatter, std::index_sequence) { Format(os, formatter.format.begin(), formatter.format.end(), std::get(formatter.args)...); } template std::ostream& operator<<(std::ostream& os, const Formatter& printer) { FormatHelper(os, printer, std::index_sequence_for{}); return os; } } template...>, int>> auto Format(std::string_view format, const Args&... args) { return Internal::Formatter{format, std::forward_as_tuple(args...)}; } }