/* file quoted.hpp * * author: Roland Conybeare, Sep 2022 */ #pragma once #include "nestlog/tostr.hpp" #include #include #include #include namespace xo { namespace print { /* use this to avoid template conversion hassles * since literal strings get treated as arrays */ template char const * ccs(T x) { return x; } /* Printing cases: * 1. T&&: * move into quoted_impl. T must be moveable! * 2. T&: * copy reference into quoted_impl. * similarly for T const &, copy reference into quoted_impl */ template class quoted_impl { public: quoted_impl(bool unq_flag, T const & x) : unq_flag_{unq_flag}, value_{x} {} quoted_impl(bool unq_flag, T && x) : unq_flag_{unq_flag}, value_{std::move(x)} {} bool unq_flag() const { return unq_flag_; } T const & value() const { return value_; } void print(std::ostream & os) const { std::string xs = xo::tostr(value_); if (xs.empty()) { /* print empty string as "" */ os << "\"\""; } else if ((xs.at(0) == '<') && (xs.at(xs.size() - 1) == '>')) { /* assume string represents output of a well-formed object printer, * and already self-escapes */ os << xs; } else if (xs.find_first_of(" \"\n\r\\") == std::string::npos) { /* no escapes needed, just print xs */ if (unq_flag_) os << xs; else os << "\"" << xs << "\""; } else { /* printed value contains a space * and/or a must-be-escaped character. * in any case, need quotes */ os << "\""; /* print contents of ss, with escapes: * \ => \\ * " => \" * newline => \n * cr => \r */ for (char ch : xs) { switch (ch) { case '"': /* " => \" */ os << "\\\""; break; case '\n': /* newline -> \n */ os << "\\\n"; break; case '\r': /* cr -> \r */ os << "\\\r"; break; case '\\': /* \ => \\ (mind c++ requires we escape \) */ os << "\\\\"; break; default: os << ch; break; } } os << "\""; } } /*print*/ private: /* .unq_flag: if true, omit surrounding " chars * if printed value satisfies both: * - no escaped chars * - no spaces */ bool unq_flag_ = false; /* .value: value to be printed */ T value_; }; /*quoted_impl*/ template std::ostream & operator<<(std::ostream & os, quoted_impl const & x) { x.print(os); return os; } /*operator*/ /* writing out std::forward behavior for completeness' sake: * * 1. call quoted(x) with rvalue std::string x, then: * - T will be deduced to [std::string] * (in particular: _not_ std::string &, std::string const &, std::string &&) * - rvalue std::string passed to quoted_impl ctor * * 2a. call quoted(x) with std::string & x, then: * - T deduced to [std::string &] * - std::string & passed to quoted_impl ctor * * 2b. call quoted(x) with std::string const & x, then: * - T deduced to [std::string const &] * - std::string const & passed to quoted_impl ctor */ template auto quoted(T && x) { return quoted_impl(false /*unq_flag*/, std::forward(x)); } inline auto qcstr(char const * x) { return quoted(x); } /*qcstr*/ template auto unq(T && x) { return quoted_impl(true /*unq_flag*/, std::forward(x)); } } /*namespace print*/ } /*namespace xo*/ /* end quoted.hpp */