219 lines
6.2 KiB
C++
219 lines
6.2 KiB
C++
/*
|
|
FORMAT-UTIL.hpp - helpers for formatting and diagnostics
|
|
|
|
Copyright (C) Lumiera.org
|
|
2009, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program 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.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
/** @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 <string>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <utility>
|
|
#include <typeinfo>
|
|
|
|
|
|
|
|
namespace util {
|
|
|
|
using lib::meta::can_IterForEach;
|
|
using std::string;
|
|
using std::forward;
|
|
using std::move;
|
|
|
|
|
|
|
|
namespace { // helper to convert arbitrary elements toString
|
|
|
|
template<class CON>
|
|
inline void
|
|
do_stringify(CON&)
|
|
{ /* do nothing */ }
|
|
|
|
template<class CON, typename X, typename...ELMS>
|
|
inline void
|
|
do_stringify(CON& container, X const& elm, ELMS const& ...args)
|
|
{
|
|
container += util::toString (elm);
|
|
do_stringify (container, args...);
|
|
}
|
|
|
|
|
|
template<class CON, typename...ELMS>
|
|
struct SeqContainer
|
|
: CON
|
|
{
|
|
void
|
|
operator+= (string&& s)
|
|
{
|
|
CON::push_back (move(s));
|
|
}
|
|
};
|
|
|
|
// most common case: use a vector container...
|
|
using std::vector;
|
|
|
|
template<typename X, typename...ELMS>
|
|
struct SeqContainer<vector<X>, ELMS...>
|
|
:vector<X>
|
|
{
|
|
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<class CON, typename...ELMS>
|
|
inline CON
|
|
stringify(ELMS const& ...elms)
|
|
{
|
|
SeqContainer<CON,ELMS...> storage;
|
|
do_stringify (storage, elms...);
|
|
return CON {move(storage)};
|
|
}
|
|
|
|
/** 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<class IT>
|
|
inline auto
|
|
stringify (IT&& src)
|
|
{
|
|
using Val = typename std::remove_reference<IT>::type::value_type;
|
|
|
|
return lib::transformIterator(forward<IT>(src), util::toString<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, lib::meta::enable_if< can_IterForEach<IT>> >
|
|
{
|
|
IT iter;
|
|
|
|
_RangeIter(IT&& srcIter)
|
|
: iter(std::forward<IT>(srcIter))
|
|
{ }
|
|
_RangeIter(IT const& srcIter)
|
|
: iter(srcIter)
|
|
{ }
|
|
|
|
};
|
|
}//(end) join helper
|
|
|
|
|
|
/**
|
|
* 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 `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<class CON>
|
|
inline string
|
|
join (CON&& coll, string const& delim =", ")
|
|
{
|
|
using Coll = typename lib::meta::Strip<CON>::TypePlain;
|
|
_RangeIter<Coll> range(std::forward<CON>(coll));
|
|
|
|
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<class X>
|
|
inline string
|
|
join (std::initializer_list<X> const&& ili, string const& delim =", ")
|
|
{
|
|
return join (ili, delim);
|
|
}
|
|
|
|
|
|
} // namespace util
|
|
#endif /*LIB_FORMAT_UTIL_H*/
|