lumiera_/src/lib/format-util.hpp
Ichthyostega 20f3252892 Upgrade: down with typename!!
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...)
2025-07-06 01:19:08 +02:00

276 lines
7.5 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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