2016-01-05 23:34:53 +01:00
|
|
|
|
/*
|
|
|
|
|
|
FormatObj - 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>
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
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.
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
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
|
|
|
|
* *****************************************************************/
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
|
|
|
|
|
/** @file format-obj.cpp
|
|
|
|
|
|
** Some implementation helpers for simple object display.
|
|
|
|
|
|
** This file provides some basic implementation functions, which
|
|
|
|
|
|
** can be called from a generic front-end (header). The reason we
|
|
|
|
|
|
** stash away some functions into an implementation unit is to keep
|
|
|
|
|
|
** the include overhead low, which helps to reduce both code size
|
|
|
|
|
|
** and compilation time. The functions here perform some formatting
|
|
|
|
|
|
** tasks commonly used from debugging and diagnostics code, both to
|
|
|
|
|
|
** investigate object contents and show types and addresses. They
|
|
|
|
|
|
** are referred from our [lightweight string converter](\ref
|
2016-01-08 01:00:05 +01:00
|
|
|
|
** util::StringConv), but also from the util::toString() function
|
2018-09-21 13:46:42 +02:00
|
|
|
|
** and more common [formatting utils](\ref format-util.hpp).
|
2016-01-05 23:34:53 +01:00
|
|
|
|
**
|
|
|
|
|
|
** @see FormatHelper_test
|
|
|
|
|
|
** @see FormatString_test
|
|
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
|
|
|
|
|
#include "lib/format-obj.hpp"
|
2022-09-27 01:51:21 +02:00
|
|
|
|
#include "lib/ios-savepoint.hpp"
|
2016-01-06 04:04:56 +01:00
|
|
|
|
//#include "lib/format-string.hpp"
|
2016-01-05 23:34:53 +01:00
|
|
|
|
#include "lib/unique-malloc-owner.hpp"
|
|
|
|
|
|
#include "lib/symbol.hpp"
|
2016-01-08 08:20:59 +01:00
|
|
|
|
#include "lib/util.hpp"
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
|
|
|
|
|
#ifdef __GNUG__
|
|
|
|
|
|
#include <cxxabi.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2024-03-14 17:32:22 +01:00
|
|
|
|
#include <limits>
|
2016-01-06 04:04:56 +01:00
|
|
|
|
#include <iomanip>
|
|
|
|
|
|
#include <sstream>
|
2016-01-05 23:34:53 +01:00
|
|
|
|
#include <string>
|
2016-01-10 00:31:13 +01:00
|
|
|
|
#include <regex>
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
2016-01-06 04:04:56 +01:00
|
|
|
|
//using util::_Fmt;
|
2016-01-08 08:20:59 +01:00
|
|
|
|
using util::removePrefix;
|
|
|
|
|
|
using util::removeSuffix;
|
2016-01-10 02:02:18 +01:00
|
|
|
|
using util::isnil;
|
2016-01-05 23:34:53 +01:00
|
|
|
|
using std::string;
|
|
|
|
|
|
|
2016-01-10 00:31:13 +01:00
|
|
|
|
using std::regex;
|
|
|
|
|
|
using std::regex_replace;
|
|
|
|
|
|
|
2016-01-06 04:04:56 +01:00
|
|
|
|
|
|
|
|
|
|
namespace { // hard-wired configuration for debugging output....
|
|
|
|
|
|
|
|
|
|
|
|
// precision for rendering of double values
|
|
|
|
|
|
const auto DIAGNOSTICS_DOUBLE_PRECISION = 8;
|
|
|
|
|
|
const auto DIAGNOSTICS_FLOAT_PRECISION = 5;
|
|
|
|
|
|
|
2016-01-06 04:51:37 +01:00
|
|
|
|
/** show only this amount of trailing bytes from an address */
|
|
|
|
|
|
const size_t DIAGNOSTICS_ADDRESS_SUFFIX_LEN = 4;
|
2024-03-14 17:32:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
2025-04-15 23:40:14 +02:00
|
|
|
|
/** maximum decimal digits able to pass through a round trip without value change */
|
2024-03-14 17:32:22 +01:00
|
|
|
|
template<typename F>
|
|
|
|
|
|
constexpr size_t PRECISION_DECIMAL = std::numeric_limits<F>::digits10;
|
|
|
|
|
|
|
2025-04-15 23:40:14 +02:00
|
|
|
|
/** decimal digits required to represent each different floating-point value completely */
|
2024-03-14 17:32:22 +01:00
|
|
|
|
template<typename F>
|
|
|
|
|
|
constexpr size_t PRECISION_COMPLETE = std::numeric_limits<F>::max_digits10;
|
2016-01-06 04:04:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-05 23:34:53 +01:00
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace meta {
|
|
|
|
|
|
|
2016-01-08 08:20:59 +01:00
|
|
|
|
// pre-allocated failure indicators, which can be returned failsafe.
|
|
|
|
|
|
|
2016-01-10 11:21:34 +01:00
|
|
|
|
extern const string BOTTOM_INDICATOR = "⟂";
|
2016-01-10 03:59:01 +01:00
|
|
|
|
extern const string FAILURE_INDICATOR = "↯";
|
|
|
|
|
|
extern const string VOID_INDICATOR = "void";
|
|
|
|
|
|
extern const string FUNCTION_INDICATOR= "Function";
|
|
|
|
|
|
|
|
|
|
|
|
extern const string BOOL_FALSE_STR = "false";
|
|
|
|
|
|
extern const string BOOL_TRUE_STR = "true";
|
2016-01-08 08:20:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-05 23:34:53 +01:00
|
|
|
|
#ifdef __GNUG__
|
|
|
|
|
|
/**
|
2018-02-01 23:08:43 +01:00
|
|
|
|
* # Implementation notes
|
|
|
|
|
|
*
|
2016-01-05 23:34:53 +01:00
|
|
|
|
* GCC / G++ subscribes to a cross-vendor ABI for C++, sometimes called the IA64 ABI
|
|
|
|
|
|
* because it happens to be the native ABI for that platform. It is summarised at
|
|
|
|
|
|
* \link http://www.codesourcery.com/cxx-abi/ mentor-embedded \endlink
|
|
|
|
|
|
* along with the current specification. For users of GCC greater than or equal to 3.x,
|
|
|
|
|
|
* entry points are exposed through the standard library in `<cxxabi.h>`
|
|
|
|
|
|
*
|
|
|
|
|
|
* This implementation relies on a vendor neutral ABI for C++ compiled programs
|
|
|
|
|
|
*
|
|
|
|
|
|
* char* abi::__cxa_demangle(const char* mangled_name,
|
|
|
|
|
|
* char* output_buffer, size_t* length,
|
|
|
|
|
|
* int* status)
|
|
|
|
|
|
*
|
|
|
|
|
|
* Parameters:
|
|
|
|
|
|
* - \c mangled_name
|
|
|
|
|
|
* NUL-terminated character string containing the name to be demangled.
|
|
|
|
|
|
* - \c output_buffer
|
|
|
|
|
|
* region of memory, allocated with \c malloc, of `*length` bytes,
|
|
|
|
|
|
* into which the demangled name is stored. If \c output_buffer is not long enough,
|
|
|
|
|
|
* it is expanded using \c realloc. output_buffer may instead be NULL; in that case,
|
|
|
|
|
|
* the demangled name is placed in a region of memory allocated with \c malloc.
|
|
|
|
|
|
* - \c length
|
|
|
|
|
|
* If length is non-NULL, the length of the buffer containing the demangled name is placed in `*length`.
|
|
|
|
|
|
* - \c status
|
|
|
|
|
|
* error flag: `*status` is set to one of the following values:
|
|
|
|
|
|
*
|
|
|
|
|
|
* 0: The demangling operation succeeded.
|
|
|
|
|
|
* -1: A memory allocation failure occurred.
|
|
|
|
|
|
* -2: mangled_name is not a valid name under the C++ ABI mangling rules.
|
|
|
|
|
|
* -3: One of the arguments is invalid.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The function returns a pointer to the start of the NUL-terminated demangled name,
|
|
|
|
|
|
* or NULL if the demangling fails. The caller is responsible for deallocating
|
|
|
|
|
|
* this memory using \c free.
|
|
|
|
|
|
*/
|
|
|
|
|
|
string
|
|
|
|
|
|
demangleCxx (Literal rawName)
|
|
|
|
|
|
{
|
|
|
|
|
|
int error = -4;
|
|
|
|
|
|
UniqueMallocOwner<char> demangled (abi::__cxa_demangle (rawName,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
&error));
|
|
|
|
|
|
return 0==error? demangled.get()
|
|
|
|
|
|
: string(rawName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
#warning "Lumiera was _not_ built with a GCC compatible compiler."
|
|
|
|
|
|
#warning "There are good chances this works without problems, but up to now, \
|
|
|
|
|
|
the Lumiera developers lacked the resources to investigate that option; \
|
|
|
|
|
|
apologies for that."
|
|
|
|
|
|
|
|
|
|
|
|
/** Fallback type-ID:
|
|
|
|
|
|
* @return unaltered internal type-ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
string
|
|
|
|
|
|
demangleCxx (Literal rawName)
|
|
|
|
|
|
{
|
|
|
|
|
|
return string (rawName);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-08 08:20:59 +01:00
|
|
|
|
|
2016-01-10 00:31:13 +01:00
|
|
|
|
|
|
|
|
|
|
/** \par implementation notes
|
|
|
|
|
|
* - we do not strip type adornments like `const`, `&` or `*`,
|
|
|
|
|
|
* however, the typical usage from within util::typeStr()
|
|
|
|
|
|
* is arranged in a way to absorb these adornments by the way
|
|
|
|
|
|
* the template signatures are defined
|
|
|
|
|
|
* - we _do_ simplify the type display and strip some obnoxious
|
|
|
|
|
|
* namespace prefixes with the help of `std::regex_replace`
|
|
|
|
|
|
* - we perform those simplifying rewrites _in place_ thus _overwriting_
|
|
|
|
|
|
* the result string. This exploits the fact that the replacements are
|
|
|
|
|
|
* _always shorter_ than what is being replaced (**beware**).
|
|
|
|
|
|
* - standard regular expressions can be [assumed][stdRegEx-threadsafe]
|
|
|
|
|
|
* to be threadsafe. Thus, we're able to build an embedded shared
|
|
|
|
|
|
* static variable _on demand_ and use the performance optimisation
|
|
|
|
|
|
* offered by the standard library
|
|
|
|
|
|
* - performance wise we'll assume the transformation happens within
|
|
|
|
|
|
* the cache, so it doesn't make much of a difference if we scan
|
|
|
|
|
|
* the same comparatively short string multiple times
|
|
|
|
|
|
*
|
2025-09-08 01:54:16 +02:00
|
|
|
|
* [stdRegEx-threadsafe]: https://stackoverflow.com/questions/15752910/is-stdregex-thread-safe
|
2016-01-10 00:31:13 +01:00
|
|
|
|
*/
|
2016-01-05 23:34:53 +01:00
|
|
|
|
string
|
|
|
|
|
|
humanReadableTypeID (Literal rawType)
|
|
|
|
|
|
{
|
2016-01-10 02:02:18 +01:00
|
|
|
|
string typeName = demangleCxx (rawType);
|
|
|
|
|
|
|
2016-01-10 11:21:34 +01:00
|
|
|
|
#define TYP_EXP "[\\w<>\\(\\):,\\s]+"
|
2016-01-10 02:02:18 +01:00
|
|
|
|
|
2016-01-10 00:31:13 +01:00
|
|
|
|
static regex commonPrefixes {"std::"
|
2016-01-10 02:02:18 +01:00
|
|
|
|
"|(\\w+::)+\\(anonymous namespace\\)::"
|
2016-01-10 00:31:13 +01:00
|
|
|
|
"|lib::meta::"
|
|
|
|
|
|
"|lib::time::"
|
|
|
|
|
|
"|lib::test::"
|
|
|
|
|
|
"|lib::diff::"
|
|
|
|
|
|
"|lib::"
|
|
|
|
|
|
"|util::"
|
2018-11-15 23:59:23 +01:00
|
|
|
|
"|steam::(asset::|mobject::(session::)?|play::)?"
|
|
|
|
|
|
"|stage::model"
|
|
|
|
|
|
"|stage::ctrl"
|
2016-01-10 03:59:01 +01:00
|
|
|
|
"|lumiera::"
|
2016-01-10 00:31:13 +01:00
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
2024-11-21 18:07:30 +01:00
|
|
|
|
static regex lolong {"long long"
|
|
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
static regex unSigned {"unsigned (\\w+)"
|
|
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
2018-09-08 16:33:21 +02:00
|
|
|
|
static regex stdString {"(__cxx11::)?basic_string<char, char_traits<char>, allocator<char>\\s*>(\\s+\\B)?"
|
2017-05-02 02:02:46 +02:00
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
2016-01-10 11:21:34 +01:00
|
|
|
|
static regex stdAllocator {"(\\w+<(" TYP_EXP ")), allocator<\\2>\\s*"
|
|
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
|
|
|
|
|
static regex mapAllocator {"(map<(" TYP_EXP "), (" TYP_EXP ")),.+allocator<pair<\\2 const, \\3>\\s*>\\s*"
|
2016-01-10 00:31:13 +01:00
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
2016-07-30 23:19:21 +02:00
|
|
|
|
static regex uniquePtr {"unique_ptr<(\\w+), default_delete<\\1>\\s*"
|
|
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
2024-11-21 18:07:30 +01:00
|
|
|
|
static regex lumieraP {"P<(\\w+), shared_ptr<\\1>\\s*"
|
2016-01-10 02:02:18 +01:00
|
|
|
|
, regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
2016-01-10 00:31:13 +01:00
|
|
|
|
|
|
|
|
|
|
auto pos = typeName.begin();
|
|
|
|
|
|
auto end = typeName.end();
|
|
|
|
|
|
|
|
|
|
|
|
end = regex_replace(pos, pos, end, commonPrefixes, "");
|
2024-11-21 18:07:30 +01:00
|
|
|
|
end = regex_replace(pos, pos, end, lolong, "llong");
|
|
|
|
|
|
end = regex_replace(pos, pos, end, unSigned, "u$1");
|
2017-05-02 02:02:46 +02:00
|
|
|
|
end = regex_replace(pos, pos, end, stdString, "string");
|
2016-01-10 00:31:13 +01:00
|
|
|
|
end = regex_replace(pos, pos, end, stdAllocator, "$1");
|
2016-01-10 11:21:34 +01:00
|
|
|
|
end = regex_replace(pos, pos, end, mapAllocator, "$1");
|
2016-07-30 23:19:21 +02:00
|
|
|
|
end = regex_replace(pos, pos, end, uniquePtr, "unique_ptr<$1");
|
2016-01-10 02:02:18 +01:00
|
|
|
|
end = regex_replace(pos, pos, end, lumieraP, "P<$1");
|
2016-01-10 00:31:13 +01:00
|
|
|
|
|
|
|
|
|
|
typeName.resize(end - typeName.begin());
|
2016-01-05 23:34:53 +01:00
|
|
|
|
return typeName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-08 08:20:59 +01:00
|
|
|
|
|
2016-01-10 02:02:18 +01:00
|
|
|
|
|
|
|
|
|
|
/** \par implementation notes
|
|
|
|
|
|
* We want to get at the name of the _most relevant_ type entity.
|
|
|
|
|
|
* This in itself is a heuristic. But we can work on the assumption,
|
|
|
|
|
|
* that we get a sequence of nested namespaces and type names, and
|
|
|
|
|
|
* we'll be interested in the last, the innermost of these types.
|
|
|
|
|
|
* In the most general case, each type could be templated, and
|
|
|
|
|
|
* thus will be followed by parameter specs enclosed in angle
|
|
|
|
|
|
* braces. Behind this spec, only type adornments will follow.
|
|
|
|
|
|
* Thus we'll inspect the string _from the back side_, skipping
|
|
|
|
|
|
* over all type parameter contents, until we reach brace
|
|
|
|
|
|
* level zero again. From this point, we have to search
|
|
|
|
|
|
* backwards to the first namespace separator `::`
|
|
|
|
|
|
* @warning we acknowledge this function can fail in various ways,
|
|
|
|
|
|
* some of which will be indicated by returning the string "void".
|
|
|
|
|
|
* But it may well happen that the returned string contains
|
|
|
|
|
|
* whitespace, superfluous punctuation or even the whole
|
|
|
|
|
|
* demangled type specification as is.
|
|
|
|
|
|
*/
|
2016-01-08 08:20:59 +01:00
|
|
|
|
string
|
|
|
|
|
|
primaryTypeComponent (Literal rawType)
|
|
|
|
|
|
{
|
|
|
|
|
|
string typeStr = demangleCxx (rawType);
|
2016-01-10 02:02:18 +01:00
|
|
|
|
|
|
|
|
|
|
removeSuffix (typeStr, " const");
|
|
|
|
|
|
removeSuffix (typeStr, " const *");
|
|
|
|
|
|
removeSuffix (typeStr, "*");
|
|
|
|
|
|
removeSuffix (typeStr, "&");
|
|
|
|
|
|
|
|
|
|
|
|
if (isnil (typeStr)) return VOID_INDICATOR;
|
2016-01-10 03:59:01 +01:00
|
|
|
|
if (')' == typeStr.back()) return FUNCTION_INDICATOR;
|
2016-01-10 02:02:18 +01:00
|
|
|
|
|
|
|
|
|
|
auto end = typeStr.end();
|
|
|
|
|
|
auto beg = typeStr.begin();
|
|
|
|
|
|
int level=0;
|
|
|
|
|
|
while (--end != beg)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ('>' == *end)
|
|
|
|
|
|
++level;
|
|
|
|
|
|
else if ('<' == *end and level>0)
|
|
|
|
|
|
--level;
|
|
|
|
|
|
else
|
|
|
|
|
|
if (level==0)
|
|
|
|
|
|
{
|
|
|
|
|
|
++end;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (end == beg) return VOID_INDICATOR;
|
|
|
|
|
|
|
|
|
|
|
|
auto pos = typeStr.rfind("::", end-beg);
|
2016-01-10 11:21:34 +01:00
|
|
|
|
typeStr = (pos==string::npos? typeStr.substr(0, end-beg)
|
2016-01-10 02:02:18 +01:00
|
|
|
|
: typeStr.substr(pos+2, (end-beg)-pos-2));
|
2016-01-08 08:20:59 +01:00
|
|
|
|
return typeStr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string
|
2016-01-10 03:59:01 +01:00
|
|
|
|
sanitisedFullTypeName (lib::Literal rawName)
|
2016-01-08 08:20:59 +01:00
|
|
|
|
{
|
2016-01-10 02:02:18 +01:00
|
|
|
|
return util::sanitise (humanReadableTypeID (rawName));
|
2016-01-08 08:20:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-10 03:59:01 +01:00
|
|
|
|
string
|
|
|
|
|
|
sanitisedSymbol (string const& text)
|
|
|
|
|
|
{
|
|
|
|
|
|
static regex identifierChars {"[A-Za-z]\\w*", regex::ECMAScript | regex::optimize};
|
|
|
|
|
|
|
|
|
|
|
|
return regex_replace (text, identifierChars, "$&", std::regex_constants::format_no_copy);
|
2018-03-29 16:57:55 +02:00
|
|
|
|
} // don't copy what does not match
|
2016-01-10 03:59:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-06 04:04:56 +01:00
|
|
|
|
}}// namespace lib::meta
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-10 02:02:18 +01:00
|
|
|
|
|
2016-01-10 03:59:01 +01:00
|
|
|
|
/* === formatting and pretty printing support utils === */
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
|
|
|
|
|
namespace util {
|
|
|
|
|
|
|
2016-01-06 04:04:56 +01:00
|
|
|
|
using std::hex;
|
|
|
|
|
|
using std::setw;
|
|
|
|
|
|
using std::right;
|
|
|
|
|
|
using std::setfill;
|
2023-11-16 18:42:36 +01:00
|
|
|
|
using std::uppercase;
|
2016-01-06 04:04:56 +01:00
|
|
|
|
using std::noshowbase;
|
2016-01-06 04:51:37 +01:00
|
|
|
|
using std::ostringstream;
|
|
|
|
|
|
using std::ostream;
|
2024-03-14 17:32:22 +01:00
|
|
|
|
|
|
|
|
|
|
template<typename F>
|
2016-01-06 04:04:56 +01:00
|
|
|
|
string
|
2024-03-14 17:32:22 +01:00
|
|
|
|
showFloatingPoint (F val, size_t precision) noexcept
|
2016-01-06 04:04:56 +01:00
|
|
|
|
try {
|
|
|
|
|
|
ostringstream buffer;
|
2024-03-14 17:32:22 +01:00
|
|
|
|
buffer.precision (precision);
|
2016-01-06 04:04:56 +01:00
|
|
|
|
buffer << val;
|
|
|
|
|
|
return buffer.str();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch(...)
|
2016-01-08 08:20:59 +01:00
|
|
|
|
{ return FAILURE_INDICATOR; }
|
2016-01-06 04:04:56 +01:00
|
|
|
|
|
2024-03-14 17:32:22 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* @return fixed point string representation, never empty
|
|
|
|
|
|
* @note we set an explicit precision, since this is a diagnostic facility
|
|
|
|
|
|
* @remarks 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)
|
|
|
|
|
|
*/
|
|
|
|
|
|
string showDouble (double val) noexcept { return showFloatingPoint (val, DIAGNOSTICS_DOUBLE_PRECISION); }
|
|
|
|
|
|
string showFloat (float val) noexcept { return showFloatingPoint (val, DIAGNOSTICS_FLOAT_PRECISION); }
|
2016-01-06 04:04:56 +01:00
|
|
|
|
|
2024-03-14 17:32:22 +01:00
|
|
|
|
string showDecimal (double val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<double>); }
|
|
|
|
|
|
string showDecimal (float val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<float>); }
|
|
|
|
|
|
string showDecimal (f128 val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<f128>); }
|
|
|
|
|
|
|
|
|
|
|
|
string showComplete (double val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<double>); }
|
|
|
|
|
|
string showComplete (float val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<float>); }
|
|
|
|
|
|
string showComplete (f128 val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<f128>); }
|
2016-01-06 04:04:56 +01:00
|
|
|
|
|
|
|
|
|
|
|
2017-01-14 04:19:58 +01:00
|
|
|
|
string
|
|
|
|
|
|
showSize (size_t val) noexcept
|
|
|
|
|
|
try {
|
|
|
|
|
|
ostringstream buffer;
|
|
|
|
|
|
buffer << val;
|
|
|
|
|
|
return buffer.str();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch(...)
|
|
|
|
|
|
{ return FAILURE_INDICATOR; }
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-06 04:51:37 +01:00
|
|
|
|
/** @note show only the trailing X bytes of any address */
|
|
|
|
|
|
ostream&
|
2024-11-14 22:10:43 +01:00
|
|
|
|
showAdr (ostream& stream, void const* addr)
|
2016-01-06 04:51:37 +01:00
|
|
|
|
{
|
2022-09-27 01:51:21 +02:00
|
|
|
|
IosSavepoint save{stream};
|
2016-01-06 04:51:37 +01:00
|
|
|
|
size_t suffix_modulus = size_t(1) << DIAGNOSTICS_ADDRESS_SUFFIX_LEN * 8;
|
|
|
|
|
|
return stream << "╲"
|
|
|
|
|
|
<< hex
|
|
|
|
|
|
<< noshowbase
|
|
|
|
|
|
<< setw (DIAGNOSTICS_ADDRESS_SUFFIX_LEN * 2) // need 2 hex digits per byte
|
|
|
|
|
|
<< setfill('_')
|
|
|
|
|
|
<< right
|
|
|
|
|
|
<< size_t(addr) % suffix_modulus;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-06 04:04:56 +01:00
|
|
|
|
string
|
2024-11-14 22:10:43 +01:00
|
|
|
|
showAdr (void const* addr) noexcept
|
2016-01-06 04:04:56 +01:00
|
|
|
|
try {
|
2016-01-06 04:51:37 +01:00
|
|
|
|
ostringstream buffer;
|
2024-11-14 22:10:43 +01:00
|
|
|
|
showAdr (buffer, addr);
|
2016-01-06 04:04:56 +01:00
|
|
|
|
return buffer.str();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch(...)
|
2016-01-08 08:20:59 +01:00
|
|
|
|
{ return FAILURE_INDICATOR; }
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-11-16 18:42:36 +01:00
|
|
|
|
string
|
|
|
|
|
|
showHash (size_t hash, uint showBytes) noexcept
|
|
|
|
|
|
try {
|
|
|
|
|
|
showBytes = util::limited (1u, showBytes, 8u);
|
|
|
|
|
|
size_t suffix_modulus = size_t(1) << showBytes * 8;
|
|
|
|
|
|
ostringstream buffer;
|
|
|
|
|
|
buffer << hex
|
|
|
|
|
|
<< uppercase
|
|
|
|
|
|
<< noshowbase
|
|
|
|
|
|
<< setw (showBytes * 2) // need 2 hex digits per byte
|
|
|
|
|
|
<< setfill('0')
|
|
|
|
|
|
<< right
|
|
|
|
|
|
<< (showBytes==8? hash : hash % suffix_modulus);
|
|
|
|
|
|
return buffer.str();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch(...)
|
|
|
|
|
|
{ return FAILURE_INDICATOR; }
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-05 23:34:53 +01:00
|
|
|
|
|
|
|
|
|
|
} // namespace util
|