From 187c20e64cb5cff5e85cd6d54db9622f275646a1 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Thu, 8 Aug 2024 10:18:18 -0500 Subject: [PATCH] add pretty type for pybind --- backend/src/Utilities/Demangle.cpp | 32 ++ backend/src/Utilities/Demangle.hpp | 19 + backend/src/Utilities/PrettyType.cpp | 34 ++ backend/src/Utilities/PrettyType.hpp | 824 +++++++++++++++++++++++++++ 4 files changed, 909 insertions(+) create mode 100644 backend/src/Utilities/Demangle.cpp create mode 100644 backend/src/Utilities/Demangle.hpp create mode 100644 backend/src/Utilities/PrettyType.cpp create mode 100644 backend/src/Utilities/PrettyType.hpp diff --git a/backend/src/Utilities/Demangle.cpp b/backend/src/Utilities/Demangle.cpp new file mode 100644 index 00000000..0c4b1a18 --- /dev/null +++ b/backend/src/Utilities/Demangle.cpp @@ -0,0 +1,32 @@ +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Utilities/Demangle.hpp" + +/// \cond +// #define DEMANGLE_USE_BOOST +/// \endcond + +#if defined(DEMANGLE_USE_BOOST) +#include +#elif defined(__clang__) || defined(__GNUG__) +#include +#endif + +#if defined(DEMANGLE_USE_BOOST) +std::string demangle(char const* name) { return boost::core::demangle(name); } +#elif defined(__clang__) || defined(__GNUG__) +// else we inline some logic of boost::core::demangle +std::string demangle(char const* name) { + int status(-1); + char* demangled_name = abi::__cxa_demangle(name, nullptr, nullptr, &status); + // status == 0 is good + std::string class_name(status ? name : demangled_name); + // Be a good boy and free + std::free(demangled_name); // NOLINT + return class_name; +} +#else +std::string demangle(char const* name) { return name; } +#endif diff --git a/backend/src/Utilities/Demangle.hpp b/backend/src/Utilities/Demangle.hpp new file mode 100644 index 00000000..faa4d435 --- /dev/null +++ b/backend/src/Utilities/Demangle.hpp @@ -0,0 +1,19 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include + +//****************************************************************************** +/*!\brief Returns the demangled name of a compilation symbol +// \ingroup UtilitiesGroup +// +// \usage + \code + auto demangled_name = demangle(typeid(int).name()); + \endcode +*/ +std::string demangle(char const* name); +//****************************************************************************** diff --git a/backend/src/Utilities/PrettyType.cpp b/backend/src/Utilities/PrettyType.cpp new file mode 100644 index 00000000..8e521d0b --- /dev/null +++ b/backend/src/Utilities/PrettyType.cpp @@ -0,0 +1,34 @@ +// Reused from SpECTRE : https://spectre-code.org/ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#include "Utilities/PrettyType.hpp" + +#include +#include + +namespace pretty_type { + + namespace detail { + + std::string extract_short_name(std::string name) { + // Remove all template arguments + const std::regex template_pattern("<[^<>]*>"); + size_t previous_size = 0; + while (name.size() != previous_size) { + previous_size = name.size(); + name = std::regex_replace(name, template_pattern, ""); + } + + // Remove namespaces, etc. + const size_t last_colon = name.rfind(':'); + if (last_colon != std::string::npos) { + name.replace(0, last_colon + 1, ""); + } + + return name; + } + + } // namespace detail + +} // namespace pretty_type diff --git a/backend/src/Utilities/PrettyType.hpp b/backend/src/Utilities/PrettyType.hpp new file mode 100644 index 00000000..e3bb68a6 --- /dev/null +++ b/backend/src/Utilities/PrettyType.hpp @@ -0,0 +1,824 @@ +// Reused from SpECTRE : https://spectre-code.org/ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Utilities/Demangle.hpp" +#include "Utilities/Requires.hpp" +#include "Utilities/TMPL.hpp" +#include "Utilities/TypeTraits/ArraySize.hpp" +#include "Utilities/TypeTraits/IsA.hpp" +#include "Utilities/TypeTraits/IsStdArray.hpp" +#include "Utilities/TypeTraits/Void.hpp" + +//============================================================================== +// +// DOXYGEN DOCUMENTATION +// +//============================================================================== + +//****************************************************************************** +/*!\defgroup PrettyTypeGroup Pretty Type + * + * \brief Pretty printing of types + */ +//****************************************************************************** + +/*! + * \ingroup PrettyTypeGroup + * \brief Contains all functions that are part of PrettyType, used for printing + * types in a pretty manner. + */ +namespace pretty_type { + + namespace detail { + //**Type definitions******************************************************** + using str_const = char const* const; // + struct Type; + /*! \endcond */ + //************************************************************************** + + template <> + struct Type { + using type = char; + static constexpr str_const type_name = {"char"}; + }; + + template <> + struct Type { + using type = signed char; + static constexpr str_const type_name = {"signed char"}; + }; + + template <> + struct Type { + using type = unsigned char; + static constexpr str_const type_name = {"unsigned char"}; + }; + + template <> + struct Type { + using type = wchar_t; + static constexpr str_const type_name = {"wchar_t"}; + }; + + template <> + struct Type { + using type = char16_t; + static constexpr str_const type_name = {"char16_t"}; + }; + + template <> + struct Type { + using type = char32_t; + static constexpr str_const type_name = {"char32_t"}; + }; + + template <> + struct Type { + using type = int; + static constexpr str_const type_name = {"int"}; + }; + + template <> + struct Type { + using type = unsigned int; + static constexpr str_const type_name = {"unsigned int"}; + }; + + template <> + struct Type { + using type = long; + static constexpr str_const type_name = {"long"}; + }; + + template <> + struct Type { + using type = unsigned long; + static constexpr str_const type_name = {"unsigned long"}; + }; + + template <> + struct Type { + using type = long long; + static constexpr str_const type_name = {"long long"}; + }; + + template <> + struct Type { + using type = unsigned long long; + static constexpr str_const type_name = {"unsigned long long"}; + }; + + template <> + struct Type { + using type = short; + static constexpr str_const type_name = {"short"}; + }; + + template <> + struct Type { + using type = unsigned short; + static constexpr str_const type_name = {"unsigned short"}; + }; + + template <> + struct Type { + using type = float; + static constexpr str_const type_name = {"float"}; + }; + + template <> + struct Type { + using type = double; + static constexpr str_const type_name = {"double"}; + }; + + template <> + struct Type { + using type = long double; + static constexpr str_const type_name = {"long double"}; + }; + + template <> + struct Type { + using type = bool; + static constexpr str_const type_name = {"bool"}; + }; + + template <> + struct Type { + using type = void; + static constexpr str_const type_name = {"void"}; + }; + + template <> + struct Type { + using type = std::string; + static constexpr str_const type_name = {"std::string"}; + }; + + //**Type definitions******************************************************** + //! Alias template type for a compile-time typelist map of (T, Type) + template + using TemplateMap_t = tmpl::map>...>; + //************************************************************************** + + //**Free functions********************************************************** + /*!\brief Adds qualifiers to the string representation of the type + // \ingroup PrettyTypeGroup + // + // \tparam T The type to be pretty printed + // \return std::string with qualified name of type + */ + template + std::string add_qualifiers() { + std::stringstream ss; + if (std::is_pointer::value) { + if (std::is_const>::value) { + ss << " const"; + } + if (std::is_volatile>::value) { + ss << " volatile"; + } + ss << "*"; + } + if (std::is_const>::value) { + ss << " const"; + } + if (std::is_volatile>::value) { + ss << " volatile"; + } + if (std::is_reference::value) { + ss << "&"; + } + return ss.str(); + } + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Helper auxiliary struct to construct pretty names of containers + * \ingroup PrettyTypeGroup + * + * \tparam T the type whose name to print + * \tparam M the map of the basic types to print + * \tparam KT the struct holding the template alias template_list which is a + * list of known specializations of construct_name for those containers + */ + template + struct construct_name; + //************************************************************************** + + template + struct construct_name< + T, M, KT, + Requires>>::value == 1>> { + static std::string get() { + constexpr str_const t = + tmpl::at>>::type_name; + std::stringstream ss; + ss << t << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires< + tmpl::has_key>>>::value == 0 and + std::is_same< + tmpl::list<>, + tmpl::find< + typename KT::template template_list>>>, + std::is_base_of>>::value>> { + static std::string get() { + std::stringstream ss; + ss << demangle(typeid(T).name()); + return ss.str(); + } + }; + + // STL Sequences + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = std::decay_t>; + static std::string get() { + std::stringstream ss; + ss << "std::vector<" + << construct_name>, + M, KT>::get(); + ss << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::array<" + << construct_name>, + M, KT>::get(); + ss << add_qualifiers(); + ss << ", " << tt::array_size::value << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = std::decay_t>; + static std::string get() { + std::stringstream ss; + ss << "std::deque<" + << construct_name>, + M, KT>::get(); + ss << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = std::decay_t>; + static std::string get() { + std::stringstream ss; + ss << "std::forward_list<" + << construct_name>, + M, KT>::get(); + ss << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = std::decay_t>; + static std::string get() { + std::stringstream ss; + ss << "std::list<" + << construct_name>, + M, KT>::get(); + ss << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + // STL Associative containers + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::map<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ", " + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::multimap<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ", " + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::multiset<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::set<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + // STL Unordered associative containers + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::unordered_map<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ", " + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::unordered_multimap<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ", " + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::unordered_multiset<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::unordered_set<" + << construct_name< + std::decay_t>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + // STL Container adaptors + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::priority_queue<" + << construct_name>, + M, KT>::get() + << add_qualifiers() << ", " + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::queue<" + << construct_name>, + M, KT>::get() + << add_qualifiers() << ", " + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::stack<" + << construct_name>, + M, KT>::get() + << add_qualifiers() << ", " + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + // STL Smart pointers + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::unique_ptr<" + << construct_name())>>, + M, KT>::get() + << add_qualifiers< + std::remove_reference_t())>>() + << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + static std::string get() { + std::stringstream ss; + ss << "std::shared_ptr<" + << construct_name())>>, + M, KT>::get() + << add_qualifiers< + std::remove_reference_t())>>() + << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + + template + struct construct_name< + T, M, KT, + Requires>>>>> { + using type = + std::decay_t>>; + using element_type = typename type::element_type; + static std::string get() { + std::stringstream ss; + ss << "std::weak_ptr<" + << construct_name>, + M, KT>::get() + << add_qualifiers() << ">"; + ss << add_qualifiers(); + return ss.str(); + } + }; + } // namespace detail + + //**Type definitions********************************************************** + /*! + * \ingroup PrettyTypeGroup + * \brief typelist of basic types that can be pretty printed + * + * These are specializations of tt::Type + */ + using basics_map = detail::TemplateMap_t< + char, signed char, unsigned char, wchar_t, char16_t, char32_t, int, + unsigned int, long, unsigned long, long long, unsigned long long, short, + unsigned short, float, double, long double, bool, std::string>; + //**************************************************************************** + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + //**************************************************************************** + /*!\brief A list of type traits to check if something is an STL member + * \ingroup PrettyTypeGroup + * + * Contains a template alias with the name template_list of type traits that + * identify STL containers that can be pretty printed + */ + struct stl_templates { + /// List of known STL classes that can be pretty printed + template + using template_list = tmpl::list< + tt::is_a, tt::is_std_array, tt::is_a, + tt::is_a, tt::is_a, + tt::is_a, tt::is_a, + tt::is_a, tt::is_a, + tt::is_a, tt::is_a, + tt::is_a, tt::is_a, + tt::is_a, tt::is_a, + tt::is_a, tt::is_a, + tt::is_a, tt::is_a>; + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL FUNCTIONS + // + //============================================================================ + //**************************************************************************** + /*!\brief Returns a string with the prettiest typename known for the type T. + * \ingroup PrettyTypeGroup + * + * Example usage: auto name = get_name(); + * + * \tparam T the type to print + * \tparam Map a tmpl::map of basic types (non-containers) and their Type + * specializations that determine how to print the type name in a pretty form + * \tparam KnownTemplates struct hold template alias tmpl::list of is_... + * that are known how to be printed pretty \return std::string containing the + * typename + */ + template + std::string get_name() { + return detail::construct_name::get(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns a string with the prettiest typename known for the runtime + * type of x. + * \ingroup PrettyTypeGroup + * + * The result will generally not be as pretty as the result of + * get_name, but this function will report the derived type of a class + * when only given a base class reference, which get_type cannot do. + */ + template + std::string get_runtime_type_name(const T& x) { + return demangle(typeid(x).name()); + } + //**************************************************************************** + + namespace detail { + std::string extract_short_name(std::string name); + } // namespace detail + + //**************************************************************************** + /*!\brief Return the "short name" of a class, that is, the name + * without template parameters or scopes. + * \ingroup PrettyTypeGroup + * + */ + template + std::string short_name() { +#define SHORT_NAME_DEMANGLE_WORKAROUND + + // We need a workaround to demangle the compiler generated names for + // short name extraction. Name demangling is expensive as it necessarily + // involves heap allocations. +#ifdef SHORT_NAME_DEMANGLE_WORKAROUND + return detail::extract_short_name(demangle(typeid(T).name())); +#else + // return detail::extract_short_name(typeid(T).name()); +#endif + +#undef SHORT_NAME_DEMANGLE_WORKAROUND + } + //**************************************************************************** + + namespace detail { + template > + struct name_helper { + static std::string name() { return pretty_type::short_name(); } + }; + + template + struct name_helper> { + static std::string name() { return T::name(); } + }; + } // namespace detail + + //**************************************************************************** + /*!\brief Return the result of the `name()` member of a class. If a class + * doesn't have a `name()` member, call `pretty_type::short_name()` + * instead. + * \ingroup PrettyTypeGroup + * + * \warning Do not use this inside the `name()` member of struct. This + * can lead to recursion as `pretty_type::name()` will call the `name()` + * member of the struct. + */ + template + std::string name() { + return detail::name_helper::name(); + } + //**************************************************************************** + +} // namespace pretty_type