LUMIERA.clone/src/lib/format-obj.hpp

331 lines
10 KiB
C++
Raw Normal View History

/*
FORMAT-OBJ.hpp - simple means to display an object
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2016, Hermann Vosseler <Ichthyostega@web.de>
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **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-obj.hpp
** Simple functions to represent objects, for debugging and diagnostics.
** The helpers provided here are rather commonplace, but written in a way
** to incur only modest header inclusion load. It should be OK to use them
** even on interface headers in widespread use.
** - util::toString() performs a failsafe to-String conversion, thereby preferring a
** built-in conversion operator, falling back to a lexical conversion (boost)
** or just an unmangled and simplified type string as default.
** - util::typedString() combines this with an always visible type display
** - lib::meta::demangleCxx() uses the built-in compiler support to translate a mangled
** type-ID (as given by `typeid(TY).name()`) into a readable, fully qualified
** C++ type name. This is only supported for GNU compatible compilers.
**
2016-01-09 04:05:14 +01:00
** @todo is it possible to stash away the `boost::lexical_cast` behind a custom facade,
** the way we did it for `boost::format`? This would reduce inclusion cost...
**
** @see FormatHelper_test
** @see [frontend for boost::format, printf-style](\ref format-string.hpp)
**
*/
#ifndef LIB_FORMAT_OBJ_H
#define LIB_FORMAT_OBJ_H
#include "lib/symbol.hpp"
#include "lib/meta/trait.hpp"
#include <boost/lexical_cast.hpp>
namespace std { // forward declaration to avoid including <iostream>
template<typename C>
struct char_traits;
template<typename C, class _TRAITS>
class basic_ostream;
using ostream = basic_ostream<char, char_traits<char>>;
}
namespace lib {
class Literal;
namespace meta {
std::string demangleCxx (lib::Literal rawName);
std::string humanReadableTypeID (lib::Literal);
std::string primaryTypeComponent (lib::Literal);
std::string sanitisedFullTypeName(lib::Literal);
}}// namespace lib::meta
namespace util {
std::string showDouble (double) noexcept;
std::string showFloat (float) noexcept;
std::string showSize (size_t) noexcept;
std::string showAdr (void const* addr) noexcept;
/** preconfigured format for pretty-printing of addresses */
std::ostream& showAdr (std::ostream&, void const* addr);
/** renders the size_t in hex, optionally only trailing bytes */
std::string showHash (size_t hash, uint showBytes=8) noexcept;
inline std::string
showHashLSB (size_t hash) noexcept
{
return showHash(hash,1);
}
std::string showDecimal (double) noexcept;
std::string showDecimal (float) noexcept;
std::string showDecimal (lflo) noexcept;
std::string showComplete (double)noexcept;
std::string showComplete (float) noexcept;
std::string showComplete (lflo) noexcept;
namespace { // helper for printing type diagnostics
template<typename X>
struct TypeDiagnostics
{
using Type = X;
static constexpr auto prefix = "";
static constexpr auto postfix = "";
};
template<typename X>
struct TypeDiagnostics<const X>
{
using Type = X;
static constexpr auto prefix = "const ";
static constexpr auto postfix = "";
};
template<typename X>
struct TypeDiagnostics<X&>
{
using Type = X;
static constexpr auto prefix = "";
static constexpr auto postfix = "&";
};
template<typename X>
struct TypeDiagnostics<X&&>
{
using Type = X;
static constexpr auto prefix = "";
static constexpr auto postfix = " &&";
};
template<typename X>
struct TypeDiagnostics<X const&>
{
using Type = X;
static constexpr auto prefix = "";
static constexpr auto postfix = " const&";
};
template<typename X>
struct TypeDiagnostics<X const&&>
{
using Type = X;
static constexpr auto prefix = "const ";
static constexpr auto postfix = " &&";
};
template<typename X>
struct TypeDiagnostics<X *>
{
using Type = X;
static constexpr auto prefix = "";
static constexpr auto postfix = " *";
};
template<typename X>
struct TypeDiagnostics<const X *>
{
using Type = X;
static constexpr auto prefix = "const ";
static constexpr auto postfix = " *";
};
template<typename X>
struct TypeDiagnostics<const X * const>
{
using Type = X;
static constexpr auto prefix = "const ";
static constexpr auto postfix = " * const";
};
template<typename X>
struct TypeDiagnostics<X * const>
{
using Type = X;
static constexpr auto prefix = "";
static constexpr auto postfix = " * const";
};
template<typename X>
struct TypeDiagnostics<X * const *>
{
using Type = X;
static constexpr auto prefix = "";
static constexpr auto postfix = " * const *";
};
}//(End)decoration helper
/** diagnostic type output, including const and similar adornments
* @warning operates after-the-fact and relies on mangled type names
* plus several heuristics. Output might thus not be entirely correct,
* especially when several levels of const, pointer and references are involved.
* If in doubt, place the test::TypeDebugger<T> to reveal the type as the compiler sees it.
* @remarks the function lib::meta::typeStr was deliberately written such as to remove type adornments
* and does not work on all kinds of reference. This helper complements this and
* attempts to preserve const-ness, pointer and reference decoration.
*/
template<typename X>
inline std::string
showType()
{
using Case = TypeDiagnostics<X>;
using Type = Case::Type;
return Case::prefix
+ lib::meta::humanReadableTypeID (typeid(Type).name())
+ Case::postfix;
}
template<typename...TS>
inline std::string
showTypes()
{
return "<| " + ((showType<TS>()+", ") + ... + "|>");
}
namespace {
/** toggle to prefer specialisation with direct lexical conversion */
template<typename X>
using enable_LexicalConversion = lib::meta::enable_if< lib::meta::use_LexicalConversion<X>>;
template<typename SP>
using show_SmartPointer = lib::meta::enable_if< lib::meta::is_smart_ptr<typename lib::meta::Strip<SP>::TypeReferred>>;
}
/* === generalise the failsafe string conversion === */
/** @note base case is defined in meta/util.hpp */
template<typename X>
struct StringConv<X, enable_LexicalConversion<X>>
{
static std::string
invoke (X const& val) noexcept
try { return boost::lexical_cast<std::string> (val); }
catch(...) { return FAILURE_INDICATOR; }
};
template<typename SP>
struct StringConv<SP, show_SmartPointer<SP>>
{
static std::string
invoke (SP const& smP) noexcept
try { return showSmartPtr (smP, lib::meta::typeSymbol(smP)); }
catch(...) { return FAILURE_INDICATOR; }
};
/** explicit specialisation to control precision of double values.
* @note we set an explicit precision, since this is a diagnostic facility
* and we typically do not want to see all digits, but, for test code,
* we do want a predictable string representation of simple fractional
* values like `0.1` (which can not be represented as binary floats)
*/
template<>
struct StringConv<double>
{
static std::string
invoke (double val) noexcept
{
return util::showDouble (val);
}
};
template<>
struct StringConv<float>
{
static std::string
invoke (float val) noexcept
{
return util::showFloat (val);
}
};
template<>
struct StringConv<bool>
{
static std::string
invoke (bool val) noexcept
{
return util::showBool (val);
}
};
/**
* get some string representation of any object, reliably.
* A custom string conversion operator is invoked, if applicable,
* while all lexically convertible types (numbers etc) are treated
* by boost::lexical_cast. For double or float values, hard wired
* rounding to a fixed number of digits will be performed, to yield
* a predictable display of printed unit-test results.
* @remark while the actual parameter is passed by const-ref,
* cv-qualifiactions and references are stripped from the type
* @note Deliberately there is no magic detection/support for pointers.
* This function _must not be overloaded_ (to avoid ambiguities
* in more elaborate template instantiations).
* @remark Since 10/2023 an attempt was made to solve this problem down
* in `StringConf<TY>`, yet this solution might be brittle. If you
* want pointers to be indicated (with address), consider using
* util::showPtr explicitly.
*/
template<typename TY>
inline std::string
toString (TY const& val) noexcept
{
using PlainVal = lib::meta::Strip<TY>::TypeReferred;
return StringConv<PlainVal>::invoke (val);
}
/**
* indicate type and possibly a (custom) conversion to string
* @return human readable type name '|' string representation.
* or just the type, when no string representation available
*/
template<typename TY>
inline std::string
typedString (TY const& val) noexcept
try {
std::string repr = StringConv<TY>::invoke (val);
return 0 == repr.rfind("«", 0)? repr
: "«"+typeStr(val)+"»|"+repr;
}
catch(...)
{ return FAILURE_INDICATOR; }
} // namespace util
#endif /*LIB_FORMAT_OBJ_H*/