diff --git a/include/sharg/detail/format_base.hpp b/include/sharg/detail/format_base.hpp index cbf9778c..c1635716 100644 --- a/include/sharg/detail/format_base.hpp +++ b/include/sharg/detail/format_base.hpp @@ -13,10 +13,9 @@ #pragma once -#include - #include #include +#include #include namespace sharg::detail @@ -67,7 +66,7 @@ class format_base else if constexpr (std::is_same_v) return "std::filesystem::path"; else - return seqan3::detail::type_name_as_string; + return sharg::detail::type_name_as_string; } /*!\brief Returns the `value_type` of the input container as a string (reflection). @@ -255,7 +254,7 @@ class format_help_base : public format_base { ++positional_option_count; derived_t().print_list_item(seqan3::detail::to_string("\\fBARGUMENT-", positional_option_count, "\\fP ", - option_type_and_list_info(value)), + option_type_and_list_info(value)), desc + // a list at the end may be empty and thus have a default value ((detail::is_container_option) diff --git a/include/sharg/detail/format_parse.hpp b/include/sharg/detail/format_parse.hpp index 8c98e62e..95de23bf 100644 --- a/include/sharg/detail/format_parse.hpp +++ b/include/sharg/detail/format_parse.hpp @@ -284,7 +284,7 @@ class format_parse : public format_base } /*!\brief Tries to parse an input string into a value using the stream `operator>>`. - * \tparam option_t Must model sharg::seqan3::input_stream_over. + * \tparam option_t Must model seqan3::input_stream_over. * \param[out] value Stores the parsed value. * \param[in] in The input argument to be parsed. * \returns sharg::option_parse_result::error if `in` could not be parsed via the stream @@ -331,7 +331,8 @@ class format_parse : public format_base }); throw user_input_error{seqan3::detail::to_string("You have chosen an invalid input value: ", in, - ". Please use one of: ", key_value_pairs | std::views::keys)}; + ". Please use one of: ", + key_value_pairs | std::views::keys)}; } else { diff --git a/include/sharg/detail/type_name_as_string.hpp b/include/sharg/detail/type_name_as_string.hpp new file mode 100644 index 00000000..7b36b061 --- /dev/null +++ b/include/sharg/detail/type_name_as_string.hpp @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/sharg-parser/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------------- + +/*!\file + * \brief Provides traits to inspect some information of a type, for example its name. + * \author Lydia Buntrock + */ + +#pragma once + +#if defined(__GNUC__) || defined(__clang__) +#include +#endif // defined(__GNUC__) || defined(__clang__) + +#include +#include +#include +#include + +#include + +namespace sharg::detail +{ + +/*!\brief Defines the human-readable name of the given type using the + [typeid](https://en.cppreference.com/w/cpp/language/typeid) operator. + * + * \tparam type The type to get the human-readable name for. + * + * \details + * + * On gcc and clang std::type_info only returns a mangled name. + * The mangled name can be converted to human-readable form using implementation-specific API such as + * abi::__cxa_demangle. In other implementations the name returned is already human-readable. + * + * \note The returned name is implementation defined and might change between different tool chains. + */ +template +inline std::string const type_name_as_string = [] () +{ + std::string demangled_name{}; +#if defined(__GNUC__) || defined(__clang__) // clang and gcc only return a mangled name. + using safe_ptr_t = std::unique_ptr>; + + // https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html + int status{}; + safe_ptr_t demangled_name_ptr{abi::__cxa_demangle(typeid(type).name(), 0, 0, &status), + [] (char * name_ptr) { free(name_ptr); }}; + + // We exclude status != 0, because this code can't be reached normally, only if there is a defect in the compiler + // itself, since the type is directly given by the compiler. See https://github.com/seqan/seqan3/pull/2311. + // LCOV_EXCL_START + if (status != 0) + return std::string{typeid(type).name()} + + " (abi::__cxa_demangle error status (" + std::to_string(status) + "): " + + (status == -1 ? "A memory allocation failure occurred." : + (status == -2 ? "mangled_name is not a valid name under the C++ ABI mangling rules." : + (status == -3 ? "One of the arguments is invalid." : "Unknown Error"))) + ")"; + // LCOV_EXCL_STOP + + demangled_name = std::string{std::addressof(*demangled_name_ptr)}; +#else // e.g. MSVC + demangled_name = typeid(type).name(); +#endif // defined(__GNUC__) || defined(__clang__) + + if constexpr (std::is_const_v>) + demangled_name += " const"; + if constexpr (std::is_lvalue_reference_v) + demangled_name += " &"; + if constexpr (std::is_rvalue_reference_v) + demangled_name += " &&"; + + return demangled_name; +}(); + +} // namespace sharg::detail diff --git a/test/unit/detail/CMakeLists.txt b/test/unit/detail/CMakeLists.txt index c8091398..1e9a35a1 100644 --- a/test/unit/detail/CMakeLists.txt +++ b/test/unit/detail/CMakeLists.txt @@ -17,5 +17,6 @@ sharg_test(format_html_test.cpp CYCLIC_DEPENDING_INCLUDES include-seqan3-argument_parser-detail-format_man.hpp) sharg_test(format_man_test.cpp) sharg_test(safe_filesystem_entry_test.cpp) +sharg_test(type_name_as_string_test.cpp) sharg_test(version_check_debug_test.cpp) sharg_test(version_check_release_test.cpp) diff --git a/test/unit/detail/type_name_as_string_test.cpp b/test/unit/detail/type_name_as_string_test.cpp new file mode 100644 index 00000000..6373734e --- /dev/null +++ b/test/unit/detail/type_name_as_string_test.cpp @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/sharg-parser/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------------- + +#include + +#include + +#include + +// Some test namespace to check if namespace information are preserved within the naming. +namespace foo +{ +template +struct bar +{}; +} // namespace foo + +// Some types to test if type inspection works as expected. +// Note that the returned name might differ between compiler vendors and thus must be adapted accordingly +// in case this tests fails for those vendors. +using reflection_types = ::testing::Types const &, foo::bar>>; + +template +class type_inspection : public ::testing::Test +{ + +public: + // Returns the name of the type according to the list of names defined above. + std::string const expected_name() const + { + if constexpr (std::is_same_v) + return "char"; + else if constexpr (std::is_same_v) + return "char16_t const"; + else if constexpr (std::is_same_v) + return "char32_t &"; + else if constexpr (std::is_same_v) + return "short*"; + else if constexpr (std::is_same_v) + return "double const* const"; + else if constexpr (std::is_same_v const &>) + return "foo::bar const &"; + else if constexpr (std::is_same_v>>) + return "foo::bar >"; + else + throw std::runtime_error{"Encountered unknown type in test."}; + } +}; + +// Register test. +TYPED_TEST_SUITE(type_inspection, reflection_types, ); + +TYPED_TEST(type_inspection, type_name_as_string) +{ + EXPECT_EQ(sharg::detail::type_name_as_string, this->expected_name()); +}