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.
This commit is contained in:
parent
50faff29a9
commit
7fcee74960
4 changed files with 149 additions and 17 deletions
|
|
@ -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 <string>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <typeinfo>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
|
@ -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<class CON, typename TOGGLE = void>
|
||||
struct _RangeIter
|
||||
{
|
||||
using StlIter = typename CON::const_iterator;
|
||||
|
||||
lib::RangeIter<StlIter> iter;
|
||||
|
||||
_RangeIter(CON const& collection)
|
||||
: iter(begin(collection), end(collection))
|
||||
{ }
|
||||
};
|
||||
|
||||
template<class IT>
|
||||
struct _RangeIter<IT, typename enable_if< can_IterForEach<IT> >::type>
|
||||
{
|
||||
IT iter;
|
||||
|
||||
_RangeIter(IT&& srcIter)
|
||||
: iter(std::forward<IT>(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<class CON>
|
||||
inline string
|
||||
join (CON&& coll, string const& delim =", ")
|
||||
{
|
||||
using Coll = typename lib::meta::Strip<CON>::Type;
|
||||
using Val = typename Coll::value_type;
|
||||
|
||||
std::function<string(Val const&)> toString = [] (Val const& val) { return str(val); };
|
||||
|
||||
_RangeIter<Coll> range(std::forward<Coll>(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
|
||||
|
|
|
|||
|
|
@ -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 <boost/operators.hpp>
|
||||
|
|
@ -181,19 +180,8 @@ namespace control {
|
|||
return undo_ && capture_ && isCaptured_;
|
||||
}
|
||||
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
if (!undo_ || !capture_)
|
||||
return "·noUNDO·";
|
||||
|
||||
if (!isCaptured_)
|
||||
return "<mem:missing>";
|
||||
|
||||
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<typename SIG, typename MEM>
|
||||
MementoTie<SIG,MEM>::operator std::string() const
|
||||
{
|
||||
if (!undo_ || !capture_)
|
||||
return "·noUNDO·";
|
||||
|
||||
if (!isCaptured_)
|
||||
return "<mem:missing>";
|
||||
|
||||
return "<"
|
||||
#ifdef LIB_FORMAT_UTIL_H
|
||||
+ util::str(memento_, "mem: ", "·memento·")
|
||||
#else
|
||||
+ std::string("memento")
|
||||
#endif
|
||||
+ ">";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
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<double> dubious;
|
||||
for (uint i=0; i<10; ++i)
|
||||
dubious.push_back(1.1*i);
|
||||
|
||||
std::function<AutoCounter(double)> justCount = [](double d){ return AutoCounter(d); };
|
||||
|
||||
|
||||
cout << join(dubious, "--+--") << endl;
|
||||
cout << join(transformIterator(eachElm(dubious)
|
||||
,justCount)) << endl;
|
||||
}
|
||||
};
|
||||
|
||||
LAUNCHER (FormatHelper_test, "unit common");
|
||||
|
|
|
|||
Loading…
Reference in a new issue