From f8b0a2aacc1226cfe58f941bbaf17a6fa787be46 Mon Sep 17 00:00:00 2001 From: DNKpp Date: Sat, 21 Jun 2025 16:29:05 +0200 Subject: [PATCH 1/5] feat: add PrintVisitor --- include/ctnp/PrintVisitor.hpp | 355 ++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 include/ctnp/PrintVisitor.hpp diff --git a/include/ctnp/PrintVisitor.hpp b/include/ctnp/PrintVisitor.hpp new file mode 100644 index 0000000..56c11b0 --- /dev/null +++ b/include/ctnp/PrintVisitor.hpp @@ -0,0 +1,355 @@ +// Copyright Dominic (DNKpp) Koepke 2025 - 2025. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#ifndef CTNP_PRINT_VISITOR_HPP +#define CTNP_PRINT_VISITOR_HPP + +#pragma once + +#include "ctnp/Lexer.hpp" +#include "ctnp/config/Config.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ctnp +{ + [[nodiscard]] + inline auto const& alias_map() + { + static std::unordered_map const aliases{ + {"(anonymous namespace)", "{anon-ns}"}, + { "{anonymous}", "{anon-ns}"}, + { "anonymous namespace", "{anon-ns}"}, + { "anonymous-namespace", "{anon-ns}"}, + { "", "lambda"} + }; + + return aliases; + } + + [[nodiscard]] + inline auto const& ignored_identifiers() + { + static std::unordered_set const collection{ + "__cxx11", + "__1"}; + + return collection; + } + + template + concept print_iterator = std::output_iterator; + + template + class PrintVisitor + { + public: + [[nodiscard]] + explicit PrintVisitor(OutIter out) noexcept(std::is_nothrow_move_constructible_v) + : m_Out{std::move(out)} + { + } + + [[nodiscard]] + constexpr OutIter out() const noexcept + { + return m_Out; + } + + constexpr void unrecognized(std::string_view const content) + { + print_identifier(content); + } + + static constexpr void begin() + { + } + + static constexpr void end() + { + } + + static constexpr void begin_type() + { + } + + static constexpr void end_type() + { + } + + constexpr void begin_scope() + { + m_Context.push_scope(); + } + + constexpr void end_scope() + { + m_Context.pop_scope(); + + if (!std::exchange(m_IgnoreNextScopeResolution, false)) + { + print_decoration("::"); + } + } + + constexpr void add_identifier(std::string_view content) + { + if (content.starts_with("{lambda(") + && content.ends_with('}')) + { + auto const closingIter = std::ranges::find(content | std::views::reverse, ')'); + print_identifier("lambda"); + print_identifier(std::string_view{closingIter.base(), content.cend() - 1}); + + return; + } + + // Lambdas can have the form `'lambda\\d*'`. Just print everything between ''. + if (constexpr std::string_view lambdaPrefix{"'lambda"}; + content.starts_with(lambdaPrefix) + && content.ends_with('\'')) + { + print_identifier(content.substr(1u, content.size() - 2u)); + + return; + } + + // Msvc yields lambdas in form of `` + if (constexpr std::string_view lambdaPrefix{"')) + { + print_identifier("lambda"); + + auto const numberBegin = content.cbegin() + lambdaPrefix.size(); + if (auto const numberEnd = std::ranges::find_if_not(numberBegin, content.cend() - 1u, lexing::is_digit); + numberBegin != numberEnd) + { + print_identifier("#"); + print_identifier({numberBegin, numberEnd}); + } + + return; + } + + if (content.starts_with('`') + && content.ends_with('\'')) + { + // msvc injects `\d+' as auxiliar namespaces. Ignore them. + if (std::ranges::all_of(content.substr(1u, content.size() - 2u), lexing::is_digit)) + { + m_IgnoreNextScopeResolution = true; + + return; + } + + content = content.substr(1u, content.size() - 2u); + } + + if (ignored_identifiers().contains(content)) + { + m_IgnoreNextScopeResolution = true; + + return; + } + + auto const& aliases = alias_map(); + if (auto const iter = aliases.find(content); + iter != aliases.cend()) + { + content = iter->second; + } + print_identifier(content); + } + + constexpr void begin_template_args([[maybe_unused]] std::ptrdiff_t const count) + { + print_decoration(0 == count ? "<" : "<..."); + m_Context.push_template_args(); + } + + constexpr void end_template_args() + { + m_Context.pop_template_args(); + print_decoration(">"); + } + + constexpr void add_arg() + { + } + + static constexpr void begin_function() + { + } + + static constexpr void end_function() + { + } + + static constexpr void begin_return_type() + { + } + + constexpr void end_return_type() + { + print_decoration(" "); + } + + constexpr void begin_function_args(std::ptrdiff_t const count) + { + print_decoration(0 == count ? "(" : "(..."); + m_Context.push_function_args(); + } + + constexpr void end_function_args() + { + m_Context.pop_function_args(); + print_decoration(")"); + } + + constexpr void begin_function_ptr() + { + print_identifier("("); + } + + constexpr void end_function_ptr() + { + print_identifier(")"); + } + + constexpr void begin_operator_identifier() + { + print_identifier("operator "); + } + + static constexpr void end_operator_identifier() + { + } + + constexpr void add_const() + { + print_decoration(" const"); + } + + constexpr void add_volatile() + { + print_decoration(" volatile"); + } + + constexpr void add_noexcept() + { + print_decoration(" noexcept"); + } + + constexpr void add_ptr() + { + print_decoration("*"); + } + + constexpr void add_lvalue_ref() + { + print_decoration("&"); + } + + constexpr void add_rvalue_ref() + { + print_decoration("&&"); + } + + private: + OutIter m_Out; + bool m_IgnoreNextScopeResolution{false}; + + class Context + { + public: + [[nodiscard]] + constexpr bool can_print_identifier() const noexcept + { + CTNP_ASSERT(0 <= m_ScopeDepth, "Invalid scope depth."); + CTNP_ASSERT(0 <= m_FunctionArgsDepth, "Invalid function-args depth."); + CTNP_ASSERT(0 <= m_TemplateArgsDepth, "Invalid template-args depth."); + + return m_ScopeDepth <= 1 + && 0 == m_FunctionArgsDepth + m_TemplateArgsDepth; + } + + [[nodiscard]] + constexpr bool can_print_decoration() const noexcept + { + CTNP_ASSERT(0 <= m_FunctionArgsDepth, "Invalid function-args depth."); + CTNP_ASSERT(0 <= m_TemplateArgsDepth, "Invalid template-args depth."); + + return 0 == m_ScopeDepth + && 0 == m_FunctionArgsDepth + m_TemplateArgsDepth; + } + + void push_scope() + { + ++m_ScopeDepth; + } + + void pop_scope() + { + CTNP_ASSERT(0 < m_ScopeDepth, "Unbalanced scope depth."); + --m_ScopeDepth; + } + + void push_function_args() + { + ++m_FunctionArgsDepth; + } + + void pop_function_args() + { + CTNP_ASSERT(0 < m_FunctionArgsDepth, "Unbalanced function-args depth."); + --m_FunctionArgsDepth; + } + + void push_template_args() + { + ++m_TemplateArgsDepth; + } + + void pop_template_args() + { + CTNP_ASSERT(0 < m_TemplateArgsDepth, "Unbalanced template-args depth."); + --m_TemplateArgsDepth; + } + + private: + int m_ScopeDepth{}; + int m_FunctionArgsDepth{}; + int m_TemplateArgsDepth{}; + }; + + Context m_Context{}; + + constexpr void print_identifier(std::string_view const text) + { + if (m_Context.can_print_identifier()) + { + m_Out = std::ranges::copy(text, std::move(m_Out)).out; + } + } + + constexpr void print_decoration(std::string_view const text) + { + if (m_Context.can_print_decoration()) + { + m_Out = std::ranges::copy(text, std::move(m_Out)).out; + } + } + }; +} + +#endif From dd2743b39962fc304fff1acae8131a175661e92b Mon Sep 17 00:00:00 2001 From: DNKpp Date: Sat, 21 Jun 2025 16:29:18 +0200 Subject: [PATCH 2/5] feat: add Prettify.hpp --- include/ctnp/Prettify.hpp | 68 ++ tests/CMakeLists.txt | 1 + tests/Prettify.cpp | 1355 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1424 insertions(+) create mode 100644 include/ctnp/Prettify.hpp create mode 100644 tests/Prettify.cpp diff --git a/include/ctnp/Prettify.hpp b/include/ctnp/Prettify.hpp new file mode 100644 index 0000000..1705b41 --- /dev/null +++ b/include/ctnp/Prettify.hpp @@ -0,0 +1,68 @@ +// Copyright Dominic (DNKpp) Koepke 2025 - 2025. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#ifndef CTNP_PRETTIFY_HPP +#define CTNP_PRETTIFY_HPP + +#pragma once + +#include "ctnp/Lexer.hpp" +#include "ctnp/PrintVisitor.hpp" +#include "ctnp/config/Config.hpp" +#include "ctnp/parsing/Parser.hpp" + +namespace ctnp +{ + namespace detail + { + [[nodiscard]] + constexpr std::string_view remove_template_details(std::string_view name) noexcept + { + if (name.ends_with(']')) + { + auto rest = name | std::views::reverse | std::views::drop(1); + if (auto const openingIter = std::ranges::find(rest, '['); + openingIter != rest.end()) + { + auto const end = std::ranges::find_if_not( + openingIter + 1, + rest.end(), + lexing::is_space); + name = std::string_view{name.cbegin(), end.base()}; + } + } + + return name; + } + } + + template + constexpr OutIter prettify_type(OutIter out, std::string_view name) + { + static_assert(parsing::parser_visitor>); + + PrintVisitor visitor{std::move(out)}; + parsing::Parser parser{std::ref(visitor), name}; + parser.parse_type(); + + return visitor.out(); + } + + template + constexpr OutIter prettify_function(OutIter out, std::string_view name) + { + name = detail::remove_template_details(name); + + static_assert(parsing::parser_visitor>); + + PrintVisitor visitor{std::move(out)}; + parsing::Parser parser{std::ref(visitor), name}; + parser.parse_function(); + + return visitor.out(); + } +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5c2968f..bcc8935 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ set(CMAKE_CXX_STANDARD ${CTNP_CXX_STANDARD}) add_executable(${TARGET_NAME} "Algorithm.cpp" "Lexer.cpp" + "Prettify.cpp" "TypeList.cpp" "Version.cpp" ) diff --git a/tests/Prettify.cpp b/tests/Prettify.cpp new file mode 100644 index 0000000..d90108a --- /dev/null +++ b/tests/Prettify.cpp @@ -0,0 +1,1355 @@ +// Copyright Dominic (DNKpp) Koepke 2025 - 2025. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#include "ctnp/Prettify.hpp" + +#if __has_include() + + #include + #include + #include + +namespace +{ + template + std::string type_name() + { + auto* const rawName = typeid(T).name(); + + // see: https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html + int status{}; + using free_deleter_t = decltype([](char* c) noexcept { std::free(c); }); + std::unique_ptr const demangledName{ + abi::__cxa_demangle(rawName, nullptr, nullptr, &status)}; + if (0 == status) + { + return {demangledName.get()}; + } + + return {rawName}; + } +} + +#else + +namespace +{ + template + std::string type_name() + { + return typeid(T).name(); + } +} + +#endif + +namespace +{ + struct outer_type + { + template + struct my_template + { + }; + + struct my_type + { + }; + + auto my_typeFunction() + { + struct my_type + { + }; + + return my_type{}; + } + + auto my_typeNoexceptFunction() noexcept + { + struct my_type + { + }; + + return my_type{}; + } + + auto my_typeConstFunction() const + { + struct my_type + { + }; + + return my_type{}; + } + + auto my_typeLvalueFunction() & + { + struct my_type + { + }; + + return my_type{}; + } + + auto my_typeConstLvalueFunction() const& + { + struct my_type + { + }; + + return my_type{}; + } + + auto my_typeRvalueFunction() && + { + struct my_type + { + }; + + return my_type{}; + } + + auto my_typeConstRvalueFunction() const&& + { + struct my_type + { + }; + + return my_type{}; + } + + static auto my_typeStaticFunction() + { + struct my_type + { + }; + + return my_type{}; + } + + auto operator+(int) + { + struct my_type + { + }; + + return my_type{}; + } + + private: + auto my_typePrivateFunction() + { + struct my_type + { + }; + + return my_type{}; + } + + public: + auto my_typeIndirectlyPrivateFunction() + { + return my_typePrivateFunction(); + } + }; + + struct my_type + { + }; + + constexpr auto my_typeLambda = [] { + struct my_type + { + }; + + return my_type{}; + }; + + [[maybe_unused]] auto my_typeMutableLambda = []() mutable { + struct my_type + { + }; + + return my_type{}; + }; + + constexpr auto my_typeNoexceptLambda = []() noexcept { + struct my_type + { + }; + + return my_type{}; + }; + + constexpr auto my_typeNestedLambda = [] { + constexpr auto inner = [] { + struct my_type + { + }; + + return my_type{}; + }; + + return inner(); + }; + + constexpr auto my_typeNestedLambda2 = [] { + [[maybe_unused]] constexpr auto dummy = [] {}; + [[maybe_unused]] constexpr auto dummy2 = [] {}; + + constexpr auto inner = [] { + struct my_type + { + }; + + return my_type{}; + }; + return inner(); + }; + + [[maybe_unused]] auto my_typeFreeFunction() + { + struct my_type + { + }; + + return my_type{}; + } + + std::string const topLevelLambdaPattern = + R"((\$_\d+|lambda(#\d+|\d+)?))"; + + std::string const lambdaScopePattern = topLevelLambdaPattern + "::"; + + std::string const anonNsScopePattern = R"(\{anon-ns\}::)"; + std::string const anonTypePattern = R"((\$_\d+|\{unnamed type#\d+\}|))"; + std::string const testCaseScopePattern = R"(CATCH2_INTERNAL_TEST_\d+::)"; + std::string const callOpScopePattern = R"(operator\s?\(\)::)"; + std::string const omittedFnArgsPattern = R"(\(\.{3}\))"; + std::string const omittedTemplateArgsPattern = R"(<\.{3}>)"; +} + +TEMPLATE_TEST_CASE( + "prettify_type handles built-in types correctly.", + "[prettify]", + char, + wchar_t, + // char8_t, this causes some linker-issues on AppleClang-16 and 17 + char16_t, + char32_t, + short, + int, + long, + long long) +{ + std::string const rawName = type_name(); + CAPTURE(rawName); + + std::ostringstream ss{}; + + SECTION("When explicit signed name is given.") + { + using T = std::make_signed_t; + std::string const name = type_name(); + CAPTURE(name); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + name); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(name)); + } + + SECTION("When unsigned name is given.") + { + using T = std::make_unsigned_t; + std::string const name = type_name(); + CAPTURE(name); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + name); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(name)); + } +} + +TEST_CASE( + "prettify_type enhances names appearance.", + "[prettify]") +{ + std::ostringstream ss{}; + + SECTION("When type-name in anonymous-namespace is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(anonNsScopePattern + "my_type")); + } + + SECTION("When anon-class is given.") + { + class + { + } constexpr anon_class [[maybe_unused]]{}; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(testCaseScopePattern + anonTypePattern)); + } + + SECTION("When anon-struct is given.") + { + class + { + } constexpr anon_struct [[maybe_unused]]{}; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(testCaseScopePattern + anonTypePattern)); + } + + SECTION("When anon-enum is given.") + { + enum + { + dummy + } constexpr anon_enum [[maybe_unused]]{}; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(testCaseScopePattern + anonTypePattern)); + } + + SECTION("When nested type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(anonNsScopePattern + "outer_type::my_type")); + } + + SECTION("When lambda is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + R"((auto )?)" + + anonNsScopePattern + + R"((?:my_typeLambda::)?)" // gcc produces this extra scope + + topLevelLambdaPattern)); + } + + SECTION("When lambda with params is given.") + { + [[maybe_unused]] constexpr auto lambda = [](std::string const&) {}; + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(testCaseScopePattern + topLevelLambdaPattern)); + } + + SECTION("When lambda-local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"((?:my_typeLambda::)?)" // gcc and clang produce this extra scope + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + } + + SECTION("When mutable lambda-local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"((?:my_typeMutableLambda::)?)" // gcc produces this extra scope + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + } + + SECTION("When noexcept lambda-local type-name is given.") + { + // noexcept doesn't seem to be part of the spec list + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"((?:my_typeNoexceptLambda::)?)" // gcc produces this extra scope + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + } + + SECTION("When nested lambda-local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"((?:my_typeNestedLambda::)?)" // gcc produces this extra scope + + lambdaScopePattern + + callOpScopePattern + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + } + + SECTION("When nested lambda-local type-name is given (more inner lambdas).") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"((?:my_typeNestedLambda2::)?)" // gcc produces this extra scope + + lambdaScopePattern + + callOpScopePattern + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + } + + SECTION("When free-function local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "my_typeFreeFunction::" + "my_type")); + } + + SECTION("When public function local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeFunction::" + "my_type")); + } + + SECTION("When public noexcept function local type-name is given.") + { + // noexcept has no effect + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeNoexceptFunction::" + "my_type")); + } + + SECTION("When public const-function local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeConstFunction::" + "my_type")); + } + + SECTION("When public static-function local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeStaticFunction::" + "my_type")); + } + + SECTION("When public lvalue-function local type-name is given.") + { + std::string const rawName = type_name().my_typeLvalueFunction())>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeLvalueFunction::" + "my_type")); + } + + SECTION("When public const lvalue-function local type-name is given.") + { + std::string const rawName = type_name().my_typeConstLvalueFunction())>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeConstLvalueFunction::" + "my_type")); + } + + SECTION("When public rvalue-function local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeRvalueFunction::" + "my_type")); + } + + SECTION("When public const rvalue-function local type-name is given.") + { + std::string const rawName = type_name().my_typeConstRvalueFunction())>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typeConstRvalueFunction::" + "my_type")); + } + + SECTION("When private function local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + "my_typePrivateFunction::" + "my_type")); + } + + SECTION("When public operator local type-name is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "outer_type::" + R"(operator\s?\+::)" + "my_type")); + } +} + +TEST_CASE( + "prettify_type enhances local type-names appearance.", + "[!mayfail][prettify]") +{ + std::ostringstream ss{}; + + SECTION("When local type is queried inside the current scope.") + { + struct my_type + { + }; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches(testCaseScopePattern + R"(my_type)")); + } + + SECTION("When local type is queried inside a lambda.") + { + std::invoke( + [&] { + struct my_type + { + }; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + std::move(ss).str(), + Catch::Matchers::Matches( + testCaseScopePattern + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + }); + } + + SECTION("When local type is queried inside a member-function.") + { + struct outer + { + void operator()(std::ostringstream& _ss) const + { + struct my_type + { + }; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{_ss}, + rawName); + REQUIRE_THAT( + std::move(_ss).str(), + Catch::Matchers::Matches( + testCaseScopePattern + + "outer::" + + callOpScopePattern + + "my_type")); + } + }; + + outer{}(ss); + } + + SECTION("When local type is queried inside a lambda with higher arity.") + { + // Todo: This case will currently fail, because parser does not handle arrays. + + int d1{}; + int d2[1]{}; + int* ptr = &d1; + std::invoke( + []( + std::ostringstream* _ss, + [[maybe_unused]] int&& ref, + [[maybe_unused]] int(&arrRef)[1], + [[maybe_unused]] int*& ptrRef) { + struct my_type + { + }; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{*_ss}, + rawName); + REQUIRE_THAT( + std::move(*_ss).str(), + Catch::Matchers::Matches( + testCaseScopePattern + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + }, + &ss, + std::move(d1), + d2, + ptr); + } + + SECTION("When local type is queried inside a nested-lambda with higher arity.") + { + std::invoke( + [](std::ostringstream* _ss) { + struct other_type + { + }; + + std::invoke( + [&]([[maybe_unused]] other_type const& dummy) { + struct my_type + { + }; + + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{*_ss}, + rawName); + REQUIRE_THAT( + std::move(*_ss).str(), + Catch::Matchers::Matches( + testCaseScopePattern + + lambdaScopePattern + + callOpScopePattern + + lambdaScopePattern + + callOpScopePattern + + "my_type")); + }, + other_type{}); + }, + &ss); + } +} + +TEST_CASE( + "prettify_type type-names enhances function type-names appearance.", + "[prettify]") +{ + std::ostringstream ss{}; + + SECTION("When function-local type is returned.") + { + using return_t = decltype(my_typeLambda()); + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"((?:my_typeLambda::)?)" // gcc produces this extra scope + + lambdaScopePattern + + callOpScopePattern + + "my_type " + R"(\(\))")); + } + + SECTION("When function-local type is parameter.") + { + using param_t = decltype(my_typeLambda()); + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches("void " + omittedFnArgsPattern)); + } +} + +TEST_CASE( + "prettify_type enhances function-pointer type-names appearance.", + "[prettify]") +{ + std::ostringstream ss{}; + + SECTION("When function-local type is returned.") + { + using return_t = decltype(my_typeLambda()); + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"((?:my_typeLambda::)?)" // gcc produces this extra scope + + lambdaScopePattern + + callOpScopePattern + + "my_type " + R"(\(\*\)\(\))")); + } + + SECTION("When function-local type is parameter.") + { + using param_t = decltype(my_typeLambda()); + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches(R"(void \(\*\))" + omittedFnArgsPattern)); + } + + SECTION("When function-ptr is returned.") + { + using ret_t = void (*)(); + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Equals("void (*)() ()")); + } + + SECTION("When function-ptr, which returns a function-ptr, is returned.") + { + using ret1_t = void (*)(); + using ret2_t = ret1_t (*)(); + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Equals("void (*)() (*)() ()")); + } +} + +namespace +{ + template + struct my_template + { + struct my_type + { + }; + + auto foo(my_type) + { + return mimicpp::util::SourceLocation{}; + } + + auto bar(my_type const&, mimicpp::util::SourceLocation* outLoc) + { + if (outLoc) + { + *outLoc = mimicpp::util::SourceLocation{}; + } + + struct bar_type + { + }; + + return bar_type{}; + } + }; +} + +TEST_CASE( + "prettify_type enhances template type-names appearance.", + "[prettify]") +{ + std::ostringstream ss{}; + + SECTION("When template name in anonymous-namespace is given.") + { + std::string const rawName = type_name>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches(anonNsScopePattern + "my_template<...>")); + } + + SECTION("When template-dependant name is given.") + { + std::string const rawName = type_name::my_type>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches(anonNsScopePattern + "my_template::my_type")); + } + + SECTION("When template-dependant member-function-pointer is given.") + { + std::string const rawName = type_name>::foo)>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + std::string const pattern = + "mimicpp::util::SourceLocation " // return type + R"(\()" + + anonNsScopePattern + + R"(my_template::\*\))" + + omittedFnArgsPattern; + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches(pattern)); + } + + SECTION("When template-dependant member-function-pointer, returning local type, is given.") + { + std::string const rawName = type_name>::bar)>(); + CAPTURE(rawName); + + std::string const returnPattern = +#if MIMICPP_DETAIL_IS_MSVC // it seems msvc applies an address instead of anonymous-namespace + R"(A0x\w+::)" +#else + anonNsScopePattern + +#endif + R"(my_template::bar::bar_type)"; + std::string const pattern = + returnPattern + + R"( \()" + + anonNsScopePattern + + R"(my_template::\*\))" + + omittedFnArgsPattern; + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches(pattern)); + } + + SECTION("When arbitrary template name is given.") + { + using type_t = decltype(my_typeLambda()); + std::string const rawName = type_name>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"(my_template)" + + omittedTemplateArgsPattern)); + } +} + +namespace +{ + class special_operators + { + public: + [[nodiscard]] + auto operator<(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + + [[nodiscard]] + auto operator<=(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + + [[nodiscard]] + auto operator>(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + + [[nodiscard]] + auto operator>=(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + + [[nodiscard]] + auto operator<(std::string) const noexcept + { + struct my_type + { + [[nodiscard]] + auto operator>=(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + }; + + return my_type{}.operator>=(42); + } + + [[nodiscard]] + auto operator<=(std::string) const noexcept + { + struct my_type + { + [[nodiscard]] + auto operator>(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + }; + + return my_type{}.operator>(42); + } + + [[nodiscard]] + auto operator>(std::string) const noexcept + { + struct my_type + { + [[nodiscard]] + auto operator<=(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + }; + + return my_type{}.operator<=(42); + } + + [[nodiscard]] + auto operator>=(std::string) const noexcept + { + struct my_type + { + [[nodiscard]] + auto operator<(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + }; + + return my_type{}.operator<(42); + } + + [[nodiscard]] + auto operator<=>(int) const noexcept + { + struct my_type + { + }; + + return my_type{}; + } + + [[nodiscard]] + auto operator()(int) const + { + struct my_type + { + }; + + return my_type{}; + } + }; +} + +TEST_CASE( + "prettify_type supports operator<, <=, >, >= and <=>.", + "[prettify]") +{ + std::ostringstream ss{}; + + SECTION("When ordering operator is used.") + { + auto const [expectedFunctionName, rawName] = GENERATE( + (table)({ + { R"(operator\s?<)", type_name()}, + {R"(operator\s?<=)", type_name()}, + { R"(operator\s?>)", type_name(42))>()}, + {R"(operator\s?>=)", type_name=(42))>()} + })); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"(special_operators::)" + + expectedFunctionName + + "::my_type")); + } + + SECTION("When nested ordering operator is used.") + { + auto const [expectedFunctionName, expectedNestedFunctionName, rawName] = GENERATE( + (table)({ + { R"(operator\s?<)", R"(operator\s?>=)", type_name()}, + {R"(operator\s?<=)", R"(operator\s?>)", type_name()}, + { R"(operator\s?>)", R"(operator\s?<=)", type_name(""))>()}, + {R"(operator\s?>=)", R"(operator\s?<)", type_name=(""))>()} + })); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + anonNsScopePattern + + R"(special_operators::)" + + expectedFunctionName + + "::my_type::" + + expectedNestedFunctionName + + "::my_type")); + } + + SECTION("When spaceship-operator is used.") + { + std::string const rawName = type_name(42))>(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "special_operators::" + + R"(operator\s?<=>::)" + + "my_type")); + } +} + +TEST_CASE( + "prettify_type supports operator().", + "[prettify]") +{ + std::ostringstream ss{}; + + SECTION("When identifier contains operator() scope.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + anonNsScopePattern + + "special_operators::" + + R"(operator\s?\(\)::)" + + "my_type")); + } + + SECTION("When member-function-pointer to operator() is given.") + { + std::string const rawName = type_name(); + CAPTURE(rawName); + + ctnp::prettify_type( + std::ostreambuf_iterator{ss}, + rawName); + + std::string const returnPattern = +#if MIMICPP_DETAIL_IS_MSVC // it seems msvc applies an address instead of anonymous-namespace + R"(A0x\w+::)" +#else + anonNsScopePattern + +#endif + R"(special_operators::operator\s?\(\)::my_type )"; + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Matches( + returnPattern + + R"(\()" + + anonNsScopePattern + + R"(special_operators::\*\))" + + omittedFnArgsPattern + + R"(\s?const)")); + } +} + +TEST_CASE( + "prettify_function omits function args with just `void` content.", + "[prettify]") +{ + std::string const name = "ret my_function(void)"; + + std::ostringstream ss{}; + ctnp::prettify_function( + std::ostreambuf_iterator{ss}, + name); + + REQUIRE_THAT( + ss.str(), + Catch::Matchers::Equals(+"ret my_function<...>()")); +} From afcd70ac269f762d779c584c59345ab44ac6129c Mon Sep 17 00:00:00 2001 From: DNKpp Date: Sat, 21 Jun 2025 17:38:31 +0200 Subject: [PATCH 3/5] test: add alternative typename-provider --- tests/CMakeLists.txt | 12 ++++ tests/Prettify.cpp | 144 ++++++++++++++++++++++++++----------------- 2 files changed, 100 insertions(+), 56 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bcc8935..a0cb853 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -52,6 +52,18 @@ if (CTNP_TESTING_COMPAT_SOURCE_LOC) ) endif () +set(CTNP_TESTING_TYPENAME_PROVIDER "typeid" CACHE STRING "") +message(DEBUG "${MESSAGE_PREFIX} CTNP_TESTING_TYPENAME_PROVIDER: ${CTNP_TESTING_TYPENAME_PROVIDER}") +if (CTNP_TESTING_TYPENAME_PROVIDER STREQUAL "typeid") + target_compile_definitions(${TARGET_NAME} PRIVATE + "CTNP_TESTING_TYPENAME_PROVIDER=1" + ) +elseif (CTNP_TESTING_TYPENAME_PROVIDER STREQUAL "source-location") + target_compile_definitions(${TARGET_NAME} PRIVATE + "CTNP_TESTING_TYPENAME_PROVIDER=2" + ) +endif () + target_precompile_headers(${TARGET_NAME} PRIVATE "$<$:>" diff --git a/tests/Prettify.cpp b/tests/Prettify.cpp index d90108a..de86269 100644 --- a/tests/Prettify.cpp +++ b/tests/Prettify.cpp @@ -5,11 +5,13 @@ #include "ctnp/Prettify.hpp" -#if __has_include() +#if CTNP_TESTING_TYPENAME_PROVIDER == 1 - #include - #include - #include + #if __has_include() + + #include + #include + #include namespace { @@ -32,7 +34,7 @@ namespace } } -#else + #else namespace { @@ -43,6 +45,34 @@ namespace } } + #endif + +#elif CTNP_TESTING_TYPENAME_PROVIDER == 2 + +namespace +{ + template + [[nodiscard]] + constexpr std::string_view type_name() noexcept + { + std::string_view const fnName = mimicpp::util::SourceLocation{}.function_name(); + + auto const fnPart = std::ranges::search(fnName, std::string_view{"type_name<"}); + auto const end = std::ranges::find( + fnName.rbegin(), + std::make_reverse_iterator(fnPart.end()), + '>'); + CTNP_ASSERT(end.base() != fnPart.end(), "Unexpected."); + std::string_view const result{fnPart.end(), end.base() - 1}; + + return result; + } +} + +#else + + #error "No provider set." + #endif namespace @@ -225,7 +255,7 @@ namespace std::string const lambdaScopePattern = topLevelLambdaPattern + "::"; std::string const anonNsScopePattern = R"(\{anon-ns\}::)"; - std::string const anonTypePattern = R"((\$_\d+|\{unnamed type#\d+\}|))"; + std::string const anonTypePattern = R"((\$_\d+|\{unnamed type#\d+\}|))"; std::string const testCaseScopePattern = R"(CATCH2_INTERNAL_TEST_\d+::)"; std::string const callOpScopePattern = R"(operator\s?\(\)::)"; std::string const omittedFnArgsPattern = R"(\(\.{3}\))"; @@ -245,7 +275,7 @@ TEMPLATE_TEST_CASE( long, long long) { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); std::ostringstream ss{}; @@ -253,7 +283,7 @@ TEMPLATE_TEST_CASE( SECTION("When explicit signed name is given.") { using T = std::make_signed_t; - std::string const name = type_name(); + auto const name = type_name(); CAPTURE(name); ctnp::prettify_type( @@ -261,13 +291,13 @@ TEMPLATE_TEST_CASE( name); REQUIRE_THAT( std::move(ss).str(), - Catch::Matchers::Matches(name)); + Catch::Matchers::Matches(std::string{name})); } SECTION("When unsigned name is given.") { using T = std::make_unsigned_t; - std::string const name = type_name(); + auto const name = type_name(); CAPTURE(name); ctnp::prettify_type( @@ -275,7 +305,7 @@ TEMPLATE_TEST_CASE( name); REQUIRE_THAT( std::move(ss).str(), - Catch::Matchers::Matches(name)); + Catch::Matchers::Matches(std::string{name})); } } @@ -287,7 +317,7 @@ TEST_CASE( SECTION("When type-name in anonymous-namespace is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -304,7 +334,7 @@ TEST_CASE( { } constexpr anon_class [[maybe_unused]]{}; - std::string const rawName = type_name(); + auto const rawName = type_name>(); CAPTURE(rawName); ctnp::prettify_type( @@ -321,7 +351,7 @@ TEST_CASE( { } constexpr anon_struct [[maybe_unused]]{}; - std::string const rawName = type_name(); + auto const rawName = type_name>(); CAPTURE(rawName); ctnp::prettify_type( @@ -339,7 +369,7 @@ TEST_CASE( dummy } constexpr anon_enum [[maybe_unused]]{}; - std::string const rawName = type_name(); + auto const rawName = type_name>(); CAPTURE(rawName); ctnp::prettify_type( @@ -352,7 +382,7 @@ TEST_CASE( SECTION("When nested type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -365,7 +395,7 @@ TEST_CASE( SECTION("When lambda is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name>(); CAPTURE(rawName); ctnp::prettify_type( @@ -383,7 +413,7 @@ TEST_CASE( SECTION("When lambda with params is given.") { [[maybe_unused]] constexpr auto lambda = [](std::string const&) {}; - std::string const rawName = type_name(); + auto const rawName = type_name>(); CAPTURE(rawName); ctnp::prettify_type( @@ -396,7 +426,7 @@ TEST_CASE( SECTION("When lambda-local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -414,7 +444,7 @@ TEST_CASE( SECTION("When mutable lambda-local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -433,7 +463,7 @@ TEST_CASE( SECTION("When noexcept lambda-local type-name is given.") { // noexcept doesn't seem to be part of the spec list - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -451,7 +481,7 @@ TEST_CASE( SECTION("When nested lambda-local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -471,7 +501,7 @@ TEST_CASE( SECTION("When nested lambda-local type-name is given (more inner lambdas).") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -491,7 +521,7 @@ TEST_CASE( SECTION("When free-function local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -507,7 +537,7 @@ TEST_CASE( SECTION("When public function local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -525,7 +555,7 @@ TEST_CASE( SECTION("When public noexcept function local type-name is given.") { // noexcept has no effect - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -542,7 +572,7 @@ TEST_CASE( SECTION("When public const-function local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -559,7 +589,7 @@ TEST_CASE( SECTION("When public static-function local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -576,7 +606,7 @@ TEST_CASE( SECTION("When public lvalue-function local type-name is given.") { - std::string const rawName = type_name().my_typeLvalueFunction())>(); + auto const rawName = type_name().my_typeLvalueFunction())>(); CAPTURE(rawName); ctnp::prettify_type( @@ -593,7 +623,7 @@ TEST_CASE( SECTION("When public const lvalue-function local type-name is given.") { - std::string const rawName = type_name().my_typeConstLvalueFunction())>(); + auto const rawName = type_name().my_typeConstLvalueFunction())>(); CAPTURE(rawName); ctnp::prettify_type( @@ -610,7 +640,7 @@ TEST_CASE( SECTION("When public rvalue-function local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -627,7 +657,7 @@ TEST_CASE( SECTION("When public const rvalue-function local type-name is given.") { - std::string const rawName = type_name().my_typeConstRvalueFunction())>(); + auto const rawName = type_name().my_typeConstRvalueFunction())>(); CAPTURE(rawName); ctnp::prettify_type( @@ -644,7 +674,7 @@ TEST_CASE( SECTION("When private function local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -661,7 +691,7 @@ TEST_CASE( SECTION("When public operator local type-name is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -689,7 +719,7 @@ TEST_CASE( { }; - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -708,7 +738,7 @@ TEST_CASE( { }; - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -734,7 +764,7 @@ TEST_CASE( { }; - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -770,7 +800,7 @@ TEST_CASE( { }; - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -804,7 +834,7 @@ TEST_CASE( { }; - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -835,7 +865,7 @@ TEST_CASE( SECTION("When function-local type is returned.") { using return_t = decltype(my_typeLambda()); - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -856,7 +886,7 @@ TEST_CASE( SECTION("When function-local type is parameter.") { using param_t = decltype(my_typeLambda()); - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -878,7 +908,7 @@ TEST_CASE( SECTION("When function-local type is returned.") { using return_t = decltype(my_typeLambda()); - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -899,7 +929,7 @@ TEST_CASE( SECTION("When function-local type is parameter.") { using param_t = decltype(my_typeLambda()); - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -914,7 +944,7 @@ TEST_CASE( SECTION("When function-ptr is returned.") { using ret_t = void (*)(); - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -930,7 +960,7 @@ TEST_CASE( { using ret1_t = void (*)(); using ret2_t = ret1_t (*)(); - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -981,7 +1011,7 @@ TEST_CASE( SECTION("When template name in anonymous-namespace is given.") { - std::string const rawName = type_name>(); + auto const rawName = type_name>(); CAPTURE(rawName); ctnp::prettify_type( @@ -994,7 +1024,7 @@ TEST_CASE( SECTION("When template-dependant name is given.") { - std::string const rawName = type_name::my_type>(); + auto const rawName = type_name::my_type>(); CAPTURE(rawName); ctnp::prettify_type( @@ -1007,7 +1037,7 @@ TEST_CASE( SECTION("When template-dependant member-function-pointer is given.") { - std::string const rawName = type_name>::foo)>(); + auto const rawName = type_name>::foo)>(); CAPTURE(rawName); ctnp::prettify_type( @@ -1028,7 +1058,7 @@ TEST_CASE( SECTION("When template-dependant member-function-pointer, returning local type, is given.") { - std::string const rawName = type_name>::bar)>(); + auto const rawName = type_name>::bar)>(); CAPTURE(rawName); std::string const returnPattern = @@ -1056,7 +1086,7 @@ TEST_CASE( SECTION("When arbitrary template name is given.") { using type_t = decltype(my_typeLambda()); - std::string const rawName = type_name>(); + auto const rawName = type_name>(); CAPTURE(rawName); ctnp::prettify_type( @@ -1218,16 +1248,18 @@ TEST_CASE( "prettify_type supports operator<, <=, >, >= and <=>.", "[prettify]") { + using Input = decltype(type_name()); + std::ostringstream ss{}; SECTION("When ordering operator is used.") { auto const [expectedFunctionName, rawName] = GENERATE( - (table)({ + (table)({ { R"(operator\s?<)", type_name()}, {R"(operator\s?<=)", type_name()}, { R"(operator\s?>)", type_name(42))>()}, - {R"(operator\s?>=)", type_name=(42))>()} + {R"(operator\s?>=)",type_name=(42))>()} })); CAPTURE(rawName); @@ -1246,7 +1278,7 @@ TEST_CASE( SECTION("When nested ordering operator is used.") { auto const [expectedFunctionName, expectedNestedFunctionName, rawName] = GENERATE( - (table)({ + (table)({ { R"(operator\s?<)", R"(operator\s?>=)", type_name()}, {R"(operator\s?<=)", R"(operator\s?>)", type_name()}, { R"(operator\s?>)", R"(operator\s?<=)", type_name(""))>()}, @@ -1270,7 +1302,7 @@ TEST_CASE( SECTION("When spaceship-operator is used.") { - std::string const rawName = type_name(42))>(); + auto const rawName = type_name(42))>(); CAPTURE(rawName); ctnp::prettify_type( @@ -1294,7 +1326,7 @@ TEST_CASE( SECTION("When identifier contains operator() scope.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( @@ -1311,7 +1343,7 @@ TEST_CASE( SECTION("When member-function-pointer to operator() is given.") { - std::string const rawName = type_name(); + auto const rawName = type_name(); CAPTURE(rawName); ctnp::prettify_type( From 52f5213f35e275ebea8d9210cd1b93e979cea797 Mon Sep 17 00:00:00 2001 From: DNKpp Date: Sat, 21 Jun 2025 18:17:23 +0200 Subject: [PATCH 4/5] fix: make source-location provider compatible with gccs std::source_location --- tests/Prettify.cpp | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/tests/Prettify.cpp b/tests/Prettify.cpp index de86269..a4b20c1 100644 --- a/tests/Prettify.cpp +++ b/tests/Prettify.cpp @@ -49,15 +49,14 @@ namespace #elif CTNP_TESTING_TYPENAME_PROVIDER == 2 -namespace +template +[[nodiscard]] +static constexpr std::string_view type_name() noexcept { - template - [[nodiscard]] - constexpr std::string_view type_name() noexcept - { - std::string_view const fnName = mimicpp::util::SourceLocation{}.function_name(); + std::string_view const fnName = mimicpp::util::SourceLocation{}.function_name(); - auto const fnPart = std::ranges::search(fnName, std::string_view{"type_name<"}); + if (auto const fnPart = std::ranges::search(fnName, std::string_view{"type_name<"})) + { auto const end = std::ranges::find( fnName.rbegin(), std::make_reverse_iterator(fnPart.end()), @@ -67,6 +66,20 @@ namespace return result; } + + if (auto const templatePart = std::ranges::search(fnName, std::string_view{"T = "})) + { + auto const end = std::ranges::find( + templatePart.end(), + fnName.cend(), + ';'); + CTNP_ASSERT(end != fnName.cend(), "Unexpected."); + std::string_view const result{templatePart.end(), end}; + + return result; + } + + return ""; } #else @@ -257,8 +270,9 @@ namespace std::string const anonNsScopePattern = R"(\{anon-ns\}::)"; std::string const anonTypePattern = R"((\$_\d+|\{unnamed type#\d+\}|))"; std::string const testCaseScopePattern = R"(CATCH2_INTERNAL_TEST_\d+::)"; - std::string const callOpScopePattern = R"(operator\s?\(\)::)"; + std::string const callOpScopePattern = R"((operator\s?\(\)::)?)"; std::string const omittedFnArgsPattern = R"(\(\.{3}\))"; + std::string const optionalOmittedFnArgsPattern = "(" + omittedFnArgsPattern + ")?"; std::string const omittedTemplateArgsPattern = R"(<\.{3}>)"; } @@ -407,7 +421,8 @@ TEST_CASE( R"((auto )?)" + anonNsScopePattern + R"((?:my_typeLambda::)?)" // gcc produces this extra scope - + topLevelLambdaPattern)); + + topLevelLambdaPattern + + R"((\(\)?))")); } SECTION("When lambda with params is given.") @@ -421,7 +436,10 @@ TEST_CASE( rawName); REQUIRE_THAT( std::move(ss).str(), - Catch::Matchers::Matches(testCaseScopePattern + topLevelLambdaPattern)); + Catch::Matchers::Matches( + testCaseScopePattern + + topLevelLambdaPattern + + optionalOmittedFnArgsPattern)); } SECTION("When lambda-local type-name is given.") @@ -1259,7 +1277,7 @@ TEST_CASE( { R"(operator\s?<)", type_name()}, {R"(operator\s?<=)", type_name()}, { R"(operator\s?>)", type_name(42))>()}, - {R"(operator\s?>=)",type_name=(42))>()} + {R"(operator\s?>=)", type_name=(42))>()} })); CAPTURE(rawName); From 57ceb9327eb4e0dc3a98e2d8c24aae1b9dfe68ef Mon Sep 17 00:00:00 2001 From: DNKpp Date: Sat, 21 Jun 2025 18:17:49 +0200 Subject: [PATCH 5/5] fix: handle gcc lambdas more general --- include/ctnp/PrintVisitor.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/ctnp/PrintVisitor.hpp b/include/ctnp/PrintVisitor.hpp index 56c11b0..b57ef1d 100644 --- a/include/ctnp/PrintVisitor.hpp +++ b/include/ctnp/PrintVisitor.hpp @@ -29,8 +29,7 @@ namespace ctnp {"(anonymous namespace)", "{anon-ns}"}, { "{anonymous}", "{anon-ns}"}, { "anonymous namespace", "{anon-ns}"}, - { "anonymous-namespace", "{anon-ns}"}, - { "", "lambda"} + { "anonymous-namespace", "{anon-ns}"} }; return aliases; @@ -141,6 +140,20 @@ namespace ctnp return; } + // gcc source-location yields lambdas in form of `` + if (std::string_view constexpr lambdaPrefix{"")) + { + print_identifier("lambda"); + + // Todo: There may be a full argument-list, which we should actually parse. + content.remove_prefix(lambdaPrefix.size()); + print_decoration(2 == content.size() ? "()" : "(...)"); + + return; + } + if (content.starts_with('`') && content.ends_with('\'')) {