Yet another chainsaw massacre. One of the most obnoxious annoyances with C++ metaprogramming is the need to insert `typename` and `template` qualifiers into most definitions, to help the compiler to cope with the syntax, which is not context-free. The recent standards adds several clarifications, so that most of these qualifiers are redundant now, at least at places where it is unambiguously clear that only a type can be given. GCC already supports most of these relaxing rules (Clang unfortunately lags way behind with support of newer language features...)
276 lines
7.5 KiB
C++
276 lines
7.5 KiB
C++
/*
|
||
FORMAT-UTIL.hpp - helpers for formatting and diagnostics
|
||
|
||
Copyright (C)
|
||
2009, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
*/
|
||
|
||
|
||
/** @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 <array>
|
||
#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
|
||
collectStr(ELMS const& ...elms)
|
||
{
|
||
SeqContainer<CON,ELMS...> storage;
|
||
do_stringify (storage, elms...);
|
||
return CON {move(storage)};
|
||
}
|
||
|
||
/** standard setup: convert to string into a vector */
|
||
template<typename...ELMS>
|
||
inline vector<string>
|
||
stringify (ELMS const& ...elms)
|
||
{
|
||
return collectStr<vector<string>> (elms...);
|
||
}
|
||
|
||
/** 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 = lib::meta::ValueTypeBinding<IT>::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 = 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) // note: copy here
|
||
: iter(srcIter)
|
||
{ }
|
||
|
||
};
|
||
}//(end) join helper
|
||
|
||
|
||
/**
|
||
* enumerate a collection's contents, separated by delimiter.
|
||
* @param coll something that is standard- or Lumiera-iterable
|
||
* @note Lumiera-iterator is copied when given by ref, otherwise moved,
|
||
* while in all other cases the source container is taken by const&
|
||
* @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 COLL>
|
||
inline string
|
||
join (COLL&& coll, string const& delim =", ")
|
||
{
|
||
using Coll = lib::meta::Strip<COLL>::TypePlain;
|
||
_RangeIter<Coll> range(std::forward<COLL>(coll)); // copies when CON is reference
|
||
|
||
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);
|
||
}
|
||
|
||
// Note: offering a variant of join with var-args would create lots of ambiguities
|
||
|
||
/** shortcut: List in parentheses, separated by comma, using temporary vector */
|
||
template<typename...ARGS>
|
||
inline string
|
||
joinArgList (ARGS const& ...args)
|
||
{
|
||
return "("+join (stringify (args...))+")";
|
||
}
|
||
|
||
/** shortcut: join directly with dashes */
|
||
template<typename...ARGS>
|
||
inline string
|
||
joinDash (ARGS const& ...args)
|
||
{
|
||
return join (stringify (args...), "-");
|
||
}
|
||
|
||
/** shortcut: join directly with dots */
|
||
template<typename...ARGS>
|
||
inline string
|
||
joinDot (ARGS const& ...args)
|
||
{
|
||
return join (stringify (args...), ".");
|
||
}
|
||
|
||
|
||
|
||
/** one-argument variant that can be forward declared... */
|
||
template<class COLL>
|
||
inline string
|
||
toStringParen (COLL&& coll)
|
||
{
|
||
return "("+join (forward<COLL> (coll))+")";
|
||
}
|
||
|
||
template<class COLL>
|
||
inline string
|
||
toStringBracket (COLL&& coll)
|
||
{
|
||
return "["+join (forward<COLL> (coll))+"]";
|
||
}
|
||
|
||
|
||
/** convenient pretty-printer for std::array instances */
|
||
template<typename T, std::size_t N>
|
||
struct StringConv<std::array<T,N>>
|
||
{
|
||
static std::string
|
||
invoke (std::array<T,N> const& arr) noexcept
|
||
{
|
||
return util::toStringBracket (arr);
|
||
}
|
||
};
|
||
|
||
|
||
} // namespace util
|
||
#endif /*LIB_FORMAT_UTIL_H*/
|