From 7fcee74960478f75322f3ff8a516c65b6a75a2ba Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 5 Jul 2015 02:44:55 +0200 Subject: [PATCH] formatting helper to join a collection into a string Ouch! Why does C++ lack the most basic everyday stuff? It needn't be performant. It needn't support some fancy higher order container. Just join the f***ing strings. use Bosst?? -- OMG!! pulls in half the metra programming library and tries to work on any concievable range like object. Just somehow our Lumiera Forward Iterators aren't "range-like" enough for boost's taste. Thus let's code up that fucking for-loop ourselves, once and forever. --- src/lib/format-util.hpp | 65 ++++++++++++++++++++++++++++ src/proc/control/memento-tie.hpp | 34 +++++++++------ tests/15library.tests | 2 + tests/library/format-helper-test.cpp | 65 ++++++++++++++++++++++++++-- 4 files changed, 149 insertions(+), 17 deletions(-) diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp index 8ead91fab..dc7ef0e5a 100644 --- a/src/lib/format-util.hpp +++ b/src/lib/format-util.hpp @@ -38,10 +38,12 @@ #define LIB_FORMAT_UTIL_H #include "lib/meta/trait.hpp" +#include "lib/itertools.hpp" #include "lib/symbol.hpp" #include "lib/util.hpp" #include +#include #include #include #include @@ -59,6 +61,7 @@ namespace util { using boost::enable_if; using lib::meta::can_ToString; using lib::meta::can_lexical2string; + using lib::meta::can_IterForEach; using lib::Symbol; using util::isnil; using std::string; @@ -143,5 +146,67 @@ namespace util { : tyStr(val); } + namespace { // helper to build range iterator on demand + template + struct _RangeIter + { + using StlIter = typename CON::const_iterator; + + lib::RangeIter iter; + + _RangeIter(CON const& collection) + : iter(begin(collection), end(collection)) + { } + }; + + template + struct _RangeIter >::type> + { + IT iter; + + _RangeIter(IT&& srcIter) + : iter(std::forward(srcIter)) + { } + + }; + } + + /** + * enumerate a collection's contents, separated by delimiter. + * @param coll something that is standard-iterable + * @return all contents converted to string and joined into + * a single string, with separators interspersed. + * @remarks based on the \c boost::join library function, + * which in turn is based on + * additionally, we use our + * \link #str failsafe string conversion \endlink + * which in turn invokes custom string conversion, + * or lexical_cast as appropriate. + */ + template + inline string + join (CON&& coll, string const& delim =", ") + { + using Coll = typename lib::meta::Strip::Type; + using Val = typename Coll::value_type; + + std::function toString = [] (Val const& val) { return str(val); }; + + _RangeIter range(std::forward(coll)); + auto strings = lib::transformIterator(range.iter, toString); + + if (!strings) return ""; + + std::ostringstream buffer; + for (string const& elm : strings) + buffer << elm << delim; + + // chop off last delimiter + size_t len = buffer.str().length(); + ASSERT (len > delim.length()); + return buffer.str().substr(0, len - delim.length()); + } + + } // namespace util #endif diff --git a/src/proc/control/memento-tie.hpp b/src/proc/control/memento-tie.hpp index 58feede94..0222f1ef8 100644 --- a/src/proc/control/memento-tie.hpp +++ b/src/proc/control/memento-tie.hpp @@ -45,7 +45,6 @@ #include "lib/meta/function-closure.hpp" #include "proc/control/command-signature.hpp" #include "lib/functor-util.hpp" -#include "lib/format-util.hpp" #include "lib/util.hpp" #include @@ -181,19 +180,8 @@ namespace control { return undo_ && capture_ && isCaptured_; } - - operator std::string() const - { - if (!undo_ || !capture_) - return "·noUNDO·"; - - if (!isCaptured_) - return ""; - - return "<" - + util::str(memento_, "mem: ", "·memento·") - + ">"; - } + /** for diagnostics: include format-util.hpp */ + operator std::string() const; /// Supporting equality comparisons... @@ -211,6 +199,24 @@ namespace control { } }; + + template + MementoTie::operator std::string() const + { + if (!undo_ || !capture_) + return "·noUNDO·"; + + if (!isCaptured_) + return ""; + + return "<" +#ifdef LIB_FORMAT_UTIL_H + + util::str(memento_, "mem: ", "·memento·") +#else + + std::string("memento") +#endif + + ">"; + } diff --git a/tests/15library.tests b/tests/15library.tests index 6cd619bd3..529acf342 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -187,6 +187,8 @@ out: ^he says: hey Joe! out: ^the truth: 0 out: ^just a number: 1.234.*e\+56 out: ^12345X +out: 0.+1.1.+2.2.+3.3.+4.4.+5.5.+6.6.+7.7.+8.8.+\-\-\+\-\-9.9 +out-lit: Nr.01(0.0), Nr.02(2.2), Nr.03(4.4), Nr.04(6.6), Nr.05(8.8), Nr.06(11.0), Nr.07(13.2), Nr.08(15.4), Nr.09(17.6), Nr.10(19.8) return: 0 END diff --git a/tests/library/format-helper-test.cpp b/tests/library/format-helper-test.cpp index c625cadea..ac4258a38 100644 --- a/tests/library/format-helper-test.cpp +++ b/tests/library/format-helper-test.cpp @@ -23,10 +23,22 @@ #include "lib/test/run.hpp" #include "lib/format-util.hpp" +#include "lib/format-string.hpp" +#include "lib/iter-adapter-stl.hpp" #include "lib/error.hpp" #include +#include +#include + +using lib::transformIterator; +using lib::iter_stl::eachElm; +using util::_Fmt; + +using std::vector; +using std::string; +using std::to_string; using std::cout; using std::endl; @@ -46,22 +58,46 @@ namespace test { + class AutoCounter + { + static uint cnt; + + uint id_; + double d_; + + public: + AutoCounter(double d) + : id_(++cnt) + , d_(d*2) + { } + + operator string() const + { + return _Fmt("Nr.%02d(%3.1f)") % id_ % d_; + } + }; + uint AutoCounter::cnt = 0; + + + /***************************************************************************//** * @test verifies the proper working of some string-formatting helper functions. * - util::str() provides a failsafe to-String conversion, preferring * an built-in conversion, falling back to just a mangled type string. * @see format-util.hpp */ - class FormatHelper_test : public Test + class FormatHelper_test + : public Test { void run (Arg) { check2String(); + checkStringJoin(); } - /** @test verify a failasfe to-string conversion. */ + /** @test verify a failsafe to-string conversion. */ void check2String () { @@ -82,7 +118,30 @@ namespace test { } - + /** @test verify delimiter separated joining of arbitrary collections. + * - the first test uses a STL container, which means we need to wrap + * into a lib::RangeIter. Moreover, lexical_cast is used to convert + * the double numbers into strings. + * - the second test uses an inline transforming iterator to build a + * series of AutoCounter objects, which provide a custom string + * conversion function. Moreover, since the transforming iterator + * conforms to the Lumiera Forward Iterator concept, we can just + * move the rvalue into the formatting function without much ado + */ + void + checkStringJoin() + { + vector dubious; + for (uint i=0; i<10; ++i) + dubious.push_back(1.1*i); + + std::function justCount = [](double d){ return AutoCounter(d); }; + + + cout << join(dubious, "--+--") << endl; + cout << join(transformIterator(eachElm(dubious) + ,justCount)) << endl; + } }; LAUNCHER (FormatHelper_test, "unit common");