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

type_name : add support for MSVC (compile time) and GCC (run time) #432

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
@file
Defines _HANA_TN_CAN_CONSTEXPR and related macros
(_HANA_TN_CONSTEXPR_IF_POSSIBLE, _HANA_SIZEOF_OR_STRLEN)

@copyright Louis Dionne 2013-2017
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#ifndef BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_COMPILER_CAPABILITIES_HPP
#define BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_COMPILER_CAPABILITIES_HPP

// only clang and MSVC support constexpr __PRETTY_FUNCTION__, gcc does not
#if defined(__clang__) || defined(_MSC_VER)
#define _HANA_TN_CAN_CONSTEXPR
#endif

// in constexpr mode, strlen is equivalent to sizeof() - 1
#ifdef _HANA_TN_CAN_CONSTEXPR
#define _HANA_TN_CONSTEXPR_IF_POSSIBLE constexpr
#define _HANA_SIZEOF_OR_STRLEN(var) sizeof(var) - 1
#else
#include <cstring> // this include is not needed in constexpr mode, save compilation time
#define _HANA_TN_CONSTEXPR_IF_POSSIBLE
#define _HANA_SIZEOF_OR_STRLEN(var) strlen(var)
#endif

#endif // !BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_COMPILER_CAPABILITIES_HPP
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
@file
Defines hana::experimental::type_name_details::type_name_impl_stringliteral().
Also defines _HANA_TN__PRETTY_FUNCTION__ and related defines.

@copyright Louis Dionne 2013-2017
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#ifndef BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_PRETTY_FUNCTION_HPP
#define BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_PRETTY_FUNCTION_HPP

#include <boost/hana/experimental/detail/type_name_stringliteral.hpp>
#include <boost/hana/experimental/detail/type_name_compiler_capabilities.hpp>
#include <cstddef>

#ifdef _MSC_VER
#define _HANA_TN__PRETTY_FUNCTION__ __FUNCSIG__
#else
#define _HANA_TN__PRETTY_FUNCTION__ __PRETTY_FUNCTION__
#endif


#if defined(__clang__)
#define _HANA_TN_PRETTY_FUNCTION_TYPE_PREFIX "boost::hana::experimental::type_name_details::stringliteral boost::hana::experimental::type_name_details::type_name_impl_stringliteral() [T = "
#define _HANA_TN_PRETTY_FUNCTION_TYPE_SUFFIX "]"
#elif defined(_MSC_VER)
#define _HANA_TN_PRETTY_FUNCTION_TYPE_PREFIX "struct boost::hana::experimental::type_name_details::stringliteral __cdecl boost::hana::experimental::type_name_details::type_name_impl_stringliteral<"
#define _HANA_TN_PRETTY_FUNCTION_TYPE_SUFFIX ">(void)"
#elif defined(__GNUC__) || defined(__GNUG__)
#define _HANA_TN_PRETTY_FUNCTION_TYPE_PREFIX "constexpr boost::hana::experimental::type_name_details::stringliteral boost::hana::experimental::type_name_details::type_name_impl_stringliteral() [with T = "
#define _HANA_TN_PRETTY_FUNCTION_TYPE_SUFFIX "]"
#else
#error "No support for this compiler."
#endif


namespace boost {
namespace hana {
namespace experimental {

namespace type_name_details {

template <typename T>
constexpr stringliteral type_name_impl_stringliteral() {
_HANA_TN_CONSTEXPR_IF_POSSIBLE char const* pretty_function = _HANA_TN__PRETTY_FUNCTION__;
_HANA_TN_CONSTEXPR_IF_POSSIBLE std::size_t total_size = _HANA_SIZEOF_OR_STRLEN(_HANA_TN__PRETTY_FUNCTION__);
_HANA_TN_CONSTEXPR_IF_POSSIBLE std::size_t prefix_size = _HANA_SIZEOF_OR_STRLEN(_HANA_TN_PRETTY_FUNCTION_TYPE_PREFIX);
_HANA_TN_CONSTEXPR_IF_POSSIBLE std::size_t suffix_size = _HANA_SIZEOF_OR_STRLEN(_HANA_TN_PRETTY_FUNCTION_TYPE_SUFFIX);
return {pretty_function + prefix_size, total_size - prefix_size - suffix_size};
}
} // end namespace type_name_details

} // namespace experimental2
} // namespace hana
} // namespace boost

#endif // !BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_PRETTY_FUNCTION_HPP
72 changes: 72 additions & 0 deletions include/boost/hana/experimental/detail/type_name_stringliteral.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
@file
Defines
hana::experimental::type_name_details::stringliteral
_HANA_TN_MAKE_STRINGLITERAL

@copyright Louis Dionne 2013-2017
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#ifndef BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_STRINGLITERAL_HPP
#define BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_STRINGLITERAL_HPP

#include <boost/hana/experimental/detail/type_name_compiler_capabilities.hpp>

#include <cstddef>
#include <string>
#include <cstring>


namespace boost {
namespace hana {
namespace experimental {
namespace type_name_details {

struct stringliteral {
char const* ptr;
std::size_t length;
};


#define _HANA_TN_MAKE_STRINGLITERAL(str_literal) stringliteral { str_literal, _HANA_SIZEOF_OR_STRLEN(str_literal);


inline constexpr std::size_t constexpr_strlen(char const * s) {
std::size_t r = 0;
while(*s++ != '\0')
r++;
return r;
}

inline constexpr bool stringliteral_equal(stringliteral const & cs1, stringliteral const & cs2) {
if (cs1.length != cs2.length)
return false;

std::size_t idx = 0;
do {
if (cs1.ptr[idx] != cs2.ptr[idx])
return false;
idx++;
} while (idx < cs1.length);
return true;
}

inline constexpr bool stringliteral_equal_sz(stringliteral const & cs1, char const * literal) {
return stringliteral_equal(
cs1,
{ literal, constexpr_strlen(literal) }
);
}

inline std::string stringliteral_to_string(stringliteral const & cs) {
return std::string(cs.ptr, cs.length);
}

} // namespace type_name_details
} // namespace experimental
} // namespace hana
} // namespace boost

#endif // !BOOST_HANA_EXPERIMENTAL_DETAIL_TYPE_NAME_STRINGLITERAL_HPP
55 changes: 21 additions & 34 deletions include/boost/hana/experimental/type_name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,39 @@ Distributed under the Boost Software License, Version 1.0.

#include <boost/hana/config.hpp>
#include <boost/hana/string.hpp>
#include <boost/hana/experimental/detail/type_name_pretty_function.hpp>

#include <cstddef>
#include <utility>


BOOST_HANA_NAMESPACE_BEGIN namespace experimental {
namespace detail {
struct cstring {
char const* ptr;
std::size_t length;
};

// Note: We substract the null terminator from the string sizes below.
template <typename T>
constexpr cstring type_name_impl2() {

#if defined(__clang__)
constexpr char const* pretty_function = __PRETTY_FUNCTION__;
constexpr std::size_t total_size = sizeof(__PRETTY_FUNCTION__) - 1;
constexpr std::size_t prefix_size = sizeof("boost::hana::experimental::detail::cstring boost::hana::experimental::detail::type_name_impl2() [T = ") - 1;
constexpr std::size_t suffix_size = sizeof("]") - 1;
#else
#error "No support for this compiler."
#endif

return {pretty_function + prefix_size, total_size - prefix_size - suffix_size};
}

BOOST_HANA_NAMESPACE_BEGIN namespace experimental {
namespace type_name_details {
template <typename T, std::size_t ...i>
auto type_name_impl1(std::index_sequence<i...>) {
constexpr auto name = detail::type_name_impl2<T>();
return hana::string<*(name.ptr + i)...>{};
auto type_name_impl(std::index_sequence<i...>) {
constexpr auto name = type_name_details::type_name_impl_stringliteral<T>();
return boost::hana::string<*(name.ptr + i)...>{};
}
} // end namespace detail
} // end namespace type_name_details

//! @ingroup group-experimental
//! Returns a `hana::string` representing the name of the given type, at
//! compile-time.
//! compile-time. This works on Clang and MSVC 2017.
//!
//! It also works on GCC, but only at run time and it will return
//! a `std::string`.
//!
//! This only works on Clang (and apparently MSVC, but Hana does not work
//! there as of writing this). Original idea taken from
//! https://github.com/Manu343726/ctti.
//! Other compilers are not supported as of writing this.
//! Original idea taken from https://github.com/Manu343726/ctti.
template <typename T>
auto type_name() {
constexpr auto name = detail::type_name_impl2<T>();
return detail::type_name_impl1<T>(std::make_index_sequence<name.length>{});
#ifdef _HANA_TN_CAN_CONSTEXPR
constexpr auto name = type_name_details::type_name_impl_stringliteral<T>();
return type_name_details::type_name_impl<T>(std::make_index_sequence<name.length>{});
#else
return type_name_details::stringliteral_to_string(
type_name_details::type_name_impl_stringliteral<T>()
);
#endif
}
} BOOST_HANA_NAMESPACE_END

Expand Down
20 changes: 13 additions & 7 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,21 @@ if (NOT Boost_FOUND)
list(APPEND EXCLUDED_PUBLIC_HEADERS ${PUBLIC_HEADERS_REQUIRING_BOOST})
endif()

# The experimental::type_name test is only supported on Clang >= 3.6 and
# AppleClang >= 7.0
if (NOT ((${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" AND
NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.6)
OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang" AND
NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 7)))
# The experimental::type_name test is not supported
# on Clang < 3.6 and AppleClang < 7.0
if (
( (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
AND (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.6) )
OR
( (${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang")
AND (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 7 ))
)
list(APPEND EXCLUDED_PUBLIC_HEADERS
"boost/hana/experimental/type_name.hpp")
list(APPEND EXCLUDED_UNIT_TESTS "experimental/type_name.cpp")
list(APPEND EXCLUDED_UNIT_TESTS
"experimental/type_name.cpp")
list(APPEND EXCLUDED_UNIT_TESTS
"experimental/type_name_stringliteral_test.cpp")
endif()

# On Windows, Clang-cl emulates a MSVC bug that causes EBO not to be applied
Expand Down
26 changes: 26 additions & 0 deletions test/experimental/type_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,34 @@ struct Template { };

template <typename T>
void check_matches(std::string const& re) {
#ifdef _HANA_TN_CAN_CONSTEXPR
std::string name = hana::to<char const*>(hana::experimental::type_name<T>());
#else
std::string name = hana::experimental::type_name<T>();
#endif
std::regex regex{re};
if (!std::regex_match(name, regex)) {
std::cerr << "type name '" << name << "' does not match regex '" << re << "'" << std::endl;
std::abort();
}
}

template <typename T>
void check_exact(std::string const& expected) {
#ifdef _HANA_TN_CAN_CONSTEXPR
std::string name = hana::to<char const*>(hana::experimental::type_name<T>());
#else
std::string name = hana::experimental::type_name<T>();
#endif
if (name != expected) {
std::cerr << "type name '" << name << "' does not match expected '" << expected << "'" << std::endl;
std::abort();
}
}


int main() {
#ifdef _HANA_TN_CAN_CONSTEXPR
// Make sure this can be obtained at compile-time
BOOST_HANA_CONSTANT_CHECK(hana::equal(
hana::experimental::type_name<void>(),
Expand All @@ -38,13 +57,20 @@ int main() {
hana::experimental::type_name<int>(),
BOOST_HANA_STRING("int")
));
#endif

// Make sure we get something reasonable
check_matches<int const>("int const|const int");
check_matches<int&>(R"(int\s*&)");
check_matches<int const&>(R"(const\s+int\s*&|int\s+const\s*&)");
check_matches<int(&)[]>(R"(int\s*\(\s*&\s*\)\s*\[\s*\])");
check_matches<int(&)[10]>(R"(int\s*\(\s*&\s*\)\s*\[\s*10\s*\])");
#ifndef _MSC_VER
check_matches<Template<void, char const*>>(R"(Template<\s*void\s*,\s*(char const|const char)\s*\*\s*>)");
check_matches<void(*)(int)>(R"(void\s*\(\s*\*\s*\)\s*\(\s*int\s*\))");
#else
// MSVC adds superfluous "struct" and/or "__cdecl" keywords
check_exact<Template<void, char const*>>("struct Template<void,char const *>");
check_exact<void(*)(int)>("void(__cdecl *)(int)");
#endif
}
Loading