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:
Fischlurch 2015-07-05 02:44:55 +02:00
parent 50faff29a9
commit 7fcee74960
4 changed files with 149 additions and 17 deletions

View file

@ -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

View file

@ -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...
@ -212,6 +200,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
+ ">";
}
}} // namespace proc::control

View file

@ -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

View file

@ -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");