/* file ppstr.hpp * * author: Roland Conybeare, Jul 2025 */ #pragma once #include "xo/indentlog/print/pretty.hpp" #include "xo/indentlog/print/pretty_vector.hpp" #include #include #include namespace xo { /** @class ppconcat * @brief Container for a tuple of values to be pretty-printed in order * @tparam Args... parameter pack of argument types * * We don't use the tuple directly, so that we don't inadvertently * usurp pretty-printing behavior for all tuples. * **/ template struct ppconcat { /** tuple to be pretty-printed. See toppstr(), toppstr2() **/ std::tuple contents_; }; namespace print { namespace detail { inline bool ppconcat_printupto_aux(print::ppstate *) { return true; } template bool ppconcat_printupto_aux(print::ppstate * pps, T && x) { if (!pps->print_upto(x)) return false; if (!pps->has_margin()) return false; return true; } template bool ppconcat_printupto_aux(print::ppstate * pps, T && x, Tn && ...rest) { if (!pps->print_upto(x)) return false; if (!pps->has_margin()) return false; return ppconcat_printupto_aux(pps, rest...); } inline void ppconcat_print_pretty_rest(print::ppstate *, std::uint32_t) {} template void ppconcat_print_pretty_rest(print::ppstate * pps, std::uint32_t ci1, T && x) { pps->newline_indent(ci1); pps->pretty(x); } template void ppconcat_print_pretty_rest(print::ppstate * pps, std::uint32_t ci1, T && x, Tn && ...rest) { pps->newline_indent(ci1); pps->pretty(x); ppconcat_print_pretty_rest(pps, ci1, rest...); } inline void ppconcat_print_pretty_aux(print::ppstate *, std::uint32_t /*ci1*/) {} template void ppconcat_print_pretty_aux(print::ppstate * pps, std::uint32_t ci1, T && x, Tn && ...rest) { // first item doesn't get extra indent. Want pretty output like: // // pretty(first) // pretty(second) // pretty(third) // .. // pps->pretty(x); ppconcat_print_pretty_rest(pps, ci1, rest...); } } /** implement pretty-printing for template @c ppconcat r**/ template struct ppdetail> { /** upto=true: * try to print @p target on one line. * return false if budget (space until right margin) exhausted * or if an embedded newline appears * * @return true on success, otherwise false. * * upto=false: * pretty-print @p target using multiple lines **/ static bool print_pretty(const ppindentinfo & ppii, ppconcat target) { if (ppii.upto()) { std::uint32_t saved = ppii.pps()->pos(); if (!ppii.pps()->has_margin()) return false; if (std::apply( [ppii](auto &&... args) { return detail::ppconcat_printupto_aux(ppii.pps(), std::forward(args)...); }, std::forward>(target.contents_) ) == false) { return false; } return ppii.pps()->scan_no_newline(saved); } else { std::apply( [ppii](auto &&... args) { detail::ppconcat_print_pretty_aux(ppii.pps(), ppii.ci1(), std::forward(args)...); }, std::forward>(target.contents_) ); return false; } } }; } /*namespace print*/ /** @return string comprised of pretty-printed sequence of values in @p args * * Example: * @code * toppstr("hello") -> "hello" * toppstr("string1", .., "string99") * -> "string1 * string2 * ... * string99" * @endcode **/ template std::string toppstr(Tn&&... args) { print::ppconfig ppc; std::stringstream ss; print::ppstate_standalone pps(&ss, 0, &ppc); // - std::decay_t remove reference and cv-qualifiers // - brace initialization of ppconcat and its contained tuple pps.pretty(ppconcat...>{{std::forward(args)...}}); return ss.str(); } /** like @ref toppstr, but use pretty-printing configuration @p ppc * * Example: * @code * ppconfig ppc{.right_margin_=20, .indent_width_=4}; * toppstr2("string1", "string2", "string3") -> * "string1 * string2 * string3" * @endcode **/ template std::string toppstr2(const print::ppconfig& ppc, Tn&&... args) { std::stringstream ss; print::ppstate_standalone pps(&ss, 0, &ppc); // - std::decay_t remove reference and cv-qualifiers // - brace initialization of ppconcat and its contained tuple pps.pretty(ppconcat...>{{std::forward(args)...}}); return ss.str(); } } /*namespace xo*/ /* end ppstr.hpp */