/* FORMAT-UTIL.hpp - helpers for formatting and diagnostics Copyright (C) 2009, Hermann Vosseler   **Lumiera** is free software; you can redistribute it and/or modify it   under the terms of the GNU General Public License as published by the   Free Software Foundation; either version 2 of the License, or (at your   option) any later version. See the file COPYING for further details. */ /** @file format-util.hpp ** Collection of small helpers and convenience shortcuts for diagnostics & formatting. ** - util::str() performs a failsafe to-String conversion, thereby preferring a ** built-in conversion operator, falling back to just a mangled type string. ** - util::join() generates an enumerating string from elements ** of an arbitrary sequence or iterable. Elements will be passed ** through our [generic string conversion](\ref util::toString) ** ** @see FormatHelper_test ** @see [frontend for boost::format, printf-style](\ref format-string.hpp) ** */ #ifndef LIB_FORMAT_UTIL_H #define LIB_FORMAT_UTIL_H #include "lib/meta/trait.hpp" #include "lib/format-obj.hpp" #include "lib/itertools.hpp" #include "lib/symbol.hpp" #include "lib/util.hpp" #include #include #include #include #include #include namespace util { using lib::meta::can_IterForEach; using std::string; using std::forward; using std::move; namespace { // helper to convert arbitrary elements toString template inline void do_stringify(CON&) { /* do nothing */ } template inline void do_stringify(CON& container, X const& elm, ELMS const& ...args) { container += util::toString (elm); do_stringify (container, args...); } template struct SeqContainer : CON { void operator+= (string&& s) { CON::push_back (move(s)); } }; // most common case: use a vector container... using std::vector; template struct SeqContainer, ELMS...> :vector { SeqContainer() { this->reserve(sizeof...(ELMS)); } void operator+= (string&& s) { this->emplace_back (move(s)); } }; }//(end) stringify helper /** convert a sequence of elements to string * @param elms sequence of arbitrary elements * @tparam CON the container type to collect the results * @return a collection of type CON, initialised by the * string representation of the given elements */ template inline CON collectStr(ELMS const& ...elms) { SeqContainer storage; do_stringify (storage, elms...); return CON {move(storage)}; } /** standard setup: convert to string into a vector */ template inline vector stringify (ELMS const& ...elms) { return collectStr> (elms...); } /** convert to string as transforming step in a pipeline * @param src a "Lumiera Forward Iterator" with arbitrary result type * @return a "Lumiera Forward Iterator" with string elements * @see FormatHelper_test::checkStringify() */ template inline auto stringify (IT&& src) { using Val = lib::meta::ValueTypeBinding::value_type; return lib::transformIterator(forward(src), util::toString); } namespace { // helper to build range iterator on demand template struct _RangeIter { using StlIter = CON::const_iterator; lib::RangeIter iter; _RangeIter(CON const& collection) : iter(begin(collection), end(collection)) { } }; template struct _RangeIter> > { IT iter; _RangeIter(IT&& srcIter) : iter(std::forward(srcIter)) { } _RangeIter(IT const& srcIter) // note: copy here : iter(srcIter) { } }; }//(end) join helper /** * enumerate a collection's contents, separated by delimiter. * @param coll something that is standard- or Lumiera-iterable * @note Lumiera-iterator is copied when given by ref, otherwise moved, * while in all other cases the source container is taken by const& * @return all contents converted to string and joined into * a single string, with separators interspersed. * @remarks based `ostringstream`; additionally, we use our * [failsafe string conversion](\ref util::str), * which in turn invokes custom string conversion, * or lexical_cast as appropriate. * @remarks alternatively, the `boost::join` library function * could be used, which works on _arbitrary sequences_, * which incurs some additional weight (both in terms * of header include and debug code size). And failures * on template substitution tend to be hard to understand, * since this _generic sequence_ concept is just so danm * absolutely generic (In fact that was the reason why I * gave up and just rolled our own `join` utility) */ template inline string join (COLL&& coll, string const& delim =", ") { using Coll = lib::meta::Strip::TypePlain; _RangeIter range(std::forward(coll)); // copies when CON is reference auto strings = stringify (std::move (range.iter)); if (!strings) return ""; std::ostringstream buffer; for ( ; strings; ++strings) buffer << *strings << delim; // chop off last delimiter size_t len = buffer.str().length(); ASSERT (len >= delim.length()); return buffer.str().substr(0, len - delim.length()); } template inline string join (std::initializer_list const&& ili, string const& delim =", ") { return join (ili, delim); } // Note: offering a variant of join with var-args would create lots of ambiguities /** shortcut: List in parentheses, separated by comma, using temporary vector */ template inline string joinArgList (ARGS const& ...args) { return "("+join (stringify (args...))+")"; } /** shortcut: join directly with dashes */ template inline string joinDash (ARGS const& ...args) { return join (stringify (args...), "-"); } /** shortcut: join directly with dots */ template inline string joinDot (ARGS const& ...args) { return join (stringify (args...), "."); } /** one-argument variant that can be forward declared... */ template inline string toStringParen (COLL&& coll) { return "("+join (forward (coll))+")"; } template inline string toStringBracket (COLL&& coll) { return "["+join (forward (coll))+"]"; } /** convenient pretty-printer for std::array instances */ template struct StringConv> { static std::string invoke (std::array const& arr) noexcept { return util::toStringBracket (arr); } }; } // namespace util #endif /*LIB_FORMAT_UTIL_H*/