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
|
#define LIB_FORMAT_UTIL_H
|
||||||
|
|
||||||
#include "lib/meta/trait.hpp"
|
#include "lib/meta/trait.hpp"
|
||||||
|
#include "lib/itertools.hpp"
|
||||||
#include "lib/symbol.hpp"
|
#include "lib/symbol.hpp"
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
@ -59,6 +61,7 @@ namespace util {
|
||||||
using boost::enable_if;
|
using boost::enable_if;
|
||||||
using lib::meta::can_ToString;
|
using lib::meta::can_ToString;
|
||||||
using lib::meta::can_lexical2string;
|
using lib::meta::can_lexical2string;
|
||||||
|
using lib::meta::can_IterForEach;
|
||||||
using lib::Symbol;
|
using lib::Symbol;
|
||||||
using util::isnil;
|
using util::isnil;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
@ -143,5 +146,67 @@ namespace util {
|
||||||
: tyStr(val);
|
: 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
|
} // namespace util
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@
|
||||||
#include "lib/meta/function-closure.hpp"
|
#include "lib/meta/function-closure.hpp"
|
||||||
#include "proc/control/command-signature.hpp"
|
#include "proc/control/command-signature.hpp"
|
||||||
#include "lib/functor-util.hpp"
|
#include "lib/functor-util.hpp"
|
||||||
#include "lib/format-util.hpp"
|
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
#include <boost/operators.hpp>
|
#include <boost/operators.hpp>
|
||||||
|
|
@ -181,19 +180,8 @@ namespace control {
|
||||||
return undo_ && capture_ && isCaptured_;
|
return undo_ && capture_ && isCaptured_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** for diagnostics: include format-util.hpp */
|
||||||
operator std::string() const
|
operator std::string() const;
|
||||||
{
|
|
||||||
if (!undo_ || !capture_)
|
|
||||||
return "·noUNDO·";
|
|
||||||
|
|
||||||
if (!isCaptured_)
|
|
||||||
return "<mem:missing>";
|
|
||||||
|
|
||||||
return "<"
|
|
||||||
+ util::str(memento_, "mem: ", "·memento·")
|
|
||||||
+ ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Supporting equality comparisons...
|
/// 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
|
}} // namespace proc::control
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,8 @@ out: ^he says: hey Joe!
|
||||||
out: ^the truth: 0
|
out: ^the truth: 0
|
||||||
out: ^just a number: 1.234.*e\+56
|
out: ^just a number: 1.234.*e\+56
|
||||||
out: ^12345X
|
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
|
return: 0
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,22 @@
|
||||||
|
|
||||||
#include "lib/test/run.hpp"
|
#include "lib/test/run.hpp"
|
||||||
#include "lib/format-util.hpp"
|
#include "lib/format-util.hpp"
|
||||||
|
#include "lib/format-string.hpp"
|
||||||
|
#include "lib/iter-adapter-stl.hpp"
|
||||||
#include "lib/error.hpp"
|
#include "lib/error.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#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::cout;
|
||||||
using std::endl;
|
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.
|
* @test verifies the proper working of some string-formatting helper functions.
|
||||||
* - util::str() provides a failsafe to-String conversion, preferring
|
* - util::str() provides a failsafe to-String conversion, preferring
|
||||||
* an built-in conversion, falling back to just a mangled type string.
|
* an built-in conversion, falling back to just a mangled type string.
|
||||||
* @see format-util.hpp
|
* @see format-util.hpp
|
||||||
*/
|
*/
|
||||||
class FormatHelper_test : public Test
|
class FormatHelper_test
|
||||||
|
: public Test
|
||||||
{
|
{
|
||||||
void
|
void
|
||||||
run (Arg)
|
run (Arg)
|
||||||
{
|
{
|
||||||
check2String();
|
check2String();
|
||||||
|
checkStringJoin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @test verify a failasfe to-string conversion. */
|
/** @test verify a failsafe to-string conversion. */
|
||||||
void
|
void
|
||||||
check2String ()
|
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");
|
LAUNCHER (FormatHelper_test, "unit common");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue