Skip to content

Commit

Permalink
Merge pull request #40 from Irallia/MISC/Remove_dependency_of_seqan3_…
Browse files Browse the repository at this point in the history
…type_name_as_string

[MISC] Remove dependency of seqan3::detail::type_name_as_string.
  • Loading branch information
smehringer authored Dec 22, 2021
2 parents c67fef4 + a2bbb08 commit 27a1533
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 6 deletions.
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>

#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());
}

1 comment on commit 27a1533

@vercel
Copy link

@vercel vercel bot commented on 27a1533 Dec 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.