lumiera_/src/lib/format-util.hpp
Ichthyostega 5a7a5a5720 DOC: fix syntax of some doxygen links
seemingly we really need the \ref in the link target expression
2018-09-21 14:33:12 +02:00

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*/