Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MISC] Remove dependency of seqan3::detail::type_name_as_string. #40

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions include/sharg/detail/format_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@

#pragma once

#include <seqan3/utility/detail/type_name_as_string.hpp>

#include <sharg/auxiliary.hpp>
#include <sharg/detail/concept.hpp>
#include <sharg/detail/type_name_as_string.hpp>
#include <sharg/validators.hpp>

namespace sharg::detail
Expand Down Expand Up @@ -67,7 +66,7 @@ class format_base
else if constexpr (std::is_same_v<type, std::filesystem::path>)
return "std::filesystem::path";
else
return seqan3::detail::type_name_as_string<value_type>;
return sharg::detail::type_name_as_string<value_type>;
}

/*!\brief Returns the `value_type` of the input container as a string (reflection).
Expand Down Expand Up @@ -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<option_type>)
Expand Down
5 changes: 3 additions & 2 deletions include/sharg/detail/format_parse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
{
Expand Down
80 changes: 80 additions & 0 deletions include/sharg/detail/type_name_as_string.hpp
Original file line number Diff line number Diff line change
@@ -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 <lydia.buntrock AT fu-berlin.de>
*/

#pragma once

#if defined(__GNUC__) || defined(__clang__)
#include <cxxabi.h>
#endif // defined(__GNUC__) || defined(__clang__)

#include <functional>
#include <memory>
#include <string>
#include <typeinfo>

#include <sharg/platform.hpp>

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 <typename type>
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<char, std::function<void(char *)>>;

// 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<std::remove_reference_t<type>>)
demangled_name += " const";
if constexpr (std::is_lvalue_reference_v<type>)
demangled_name += " &";
if constexpr (std::is_rvalue_reference_v<type>)
demangled_name += " &&";

return demangled_name;
}();

} // namespace sharg::detail
1 change: 1 addition & 0 deletions test/unit/detail/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
61 changes: 61 additions & 0 deletions test/unit/detail/type_name_as_string_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <gtest/gtest.h>

eseiler marked this conversation as resolved.
Show resolved Hide resolved
#include <type_traits>

#include <sharg/detail/type_name_as_string.hpp>

// Some test namespace to check if namespace information are preserved within the naming.
namespace foo
{
template <typename ...type>
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<char, char16_t const, char32_t &, short *, double const * const,
foo::bar<char> const &, foo::bar<foo::bar<char, double>>>;

template <typename param_type>
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<param_type, char>)
return "char";
else if constexpr (std::is_same_v<param_type, char16_t const>)
return "char16_t const";
else if constexpr (std::is_same_v<param_type, char32_t &>)
return "char32_t &";
else if constexpr (std::is_same_v<param_type, short *>)
return "short*";
else if constexpr (std::is_same_v<param_type, double const * const>)
return "double const* const";
else if constexpr (std::is_same_v<param_type, foo::bar<char> const &>)
return "foo::bar<char> const &";
else if constexpr (std::is_same_v<param_type, foo::bar<foo::bar<char, double>>>)
return "foo::bar<foo::bar<char, double> >";
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<TypeParam>, this->expected_name());
}