libcruft-util/introspection/name.hpp

213 lines
7.7 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019-2020 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "../algo/search.hpp"
#include "../std.hpp"
#include "../platform.hpp"
#include <string_view>
#include <algorithm>
///////////////////////////////////////////////////////////////////////////////
namespace cruft::introspection::name {
template <typename T>
constexpr
std::string_view full (void)
{
std::string_view pretty_function (__PRETTY_FUNCTION__);
#ifdef __clang__
// PRETTY_FUNCTION = std::string_view cruft::type_name() [T = std::__1::vector<int, std::__1::allocator<int> >]
std::string_view const prefix = "[T = ";
#elif defined(__GNUC__)
// PRETTY_FUNCTION = "constexpr std::string_view cruft::type_name() [with T = std::__debug::vector<int>; std::string_view = std::basic_string_view<char>]"
std::string_view prefix = "[with T = ";
#else
#error "Unsupported compiler"
#endif
// Find the location where the type begins.
auto const type_begin = std::search (
pretty_function.begin (),
pretty_function.end (),
prefix.begin (),
prefix.end ()
) + prefix.size ();
// Find the point the type name ends. Both use ']', but gcc lists
// std::string_view in the function signature too and so requires the
// delimiting ';' as a suffix.
char const suffixes[] = ";]";
auto const type_end = std::find_first_of (
type_begin,
pretty_function.end (),
std::begin (suffixes),
std::end (suffixes)
);
// Find the start of the first template parameter so we can cut it out.
// If this isn't present we end up with a pointer to the end of the
// type string which is the end of the type anyway.
auto const template_start = std::find (type_begin, type_end, '<');
auto const template_end = cruft::search::balanced (template_start, type_end, '<', '>');
return std::string_view (
type_begin,
std::distance (type_begin, template_end)
);
}
template <typename T>
constexpr
std::string_view bare (void)
{
auto const fullname = full<T> ();
constexpr char const namespace_symbol[] = "::";
auto const last_namespace_pos = std::search (
std::rbegin (fullname),
std::rend (fullname),
namespace_symbol + 0,
namespace_symbol + 2
);
auto const length = std::distance (std::rbegin (fullname), last_namespace_pos);
auto const offset = fullname.size () - length;
return std::string_view (fullname.data () + offset, length);
}
template <> constexpr std::string_view bare<i08> (void) { return "i08"; }
template <> constexpr std::string_view bare<i16> (void) { return "i16"; }
template <> constexpr std::string_view bare<i32> (void) { return "i32"; }
template <> constexpr std::string_view bare<i64> (void) { return "i64"; }
template <> constexpr std::string_view bare<u08> (void) { return "u08"; }
template <> constexpr std::string_view bare<u16> (void) { return "u16"; }
template <> constexpr std::string_view bare<u32> (void) { return "u32"; }
template <> constexpr std::string_view bare<u64> (void) { return "u64"; }
template <> constexpr std::string_view bare<f32> (void) { return "f32"; }
template <> constexpr std::string_view bare<f64> (void) { return "f64"; }
///////////////////////////////////////////////////////////////////////////////
namespace detail {
template <std::size_t N>
constexpr std::size_t
rfind (char const (&str)[N], char target, int offset = N)
{
static_assert (N > 1);
for (std::size_t i = N - 1 - (N - offset); i; --i)
if (str[i] == target)
return i;
return 0;
}
}
///////////////////////////////////////////////////////////////////////////
template <typename T>
struct pointer_to_member_type { };
template <typename A, typename B>
struct pointer_to_member_type<A B::*> { using type = A; };
template <typename T>
struct pointer_to_member_class { };
template <typename A, typename B>
struct pointer_to_member_class<A B::*> { using type = B; };
///------------------------------------------------------------------------
/// Returns a stringview containing the name of the member pointed to by
/// the pointer-tomember.
template <typename TypeT, typename KlassT, TypeT KlassT::*Ptr>
constexpr std::string_view
member (void)
{
#if defined(COMPILER_GCC)
// GCC: "constexpr std::string_view cruft::introspection::name::member() [with TypeT = int; KlassT = foo; TypeT KlassT::* Ptr = &foo::a; std::string_view = std::basic_string_view<char>]"
//
// We find the last semicolon, then count back to the colon.
constexpr std::size_t b = detail::rfind (__PRETTY_FUNCTION__, ';');
constexpr std::size_t a = detail::rfind (__PRETTY_FUNCTION__, ':', b) + 1;
constexpr std::string_view name = __PRETTY_FUNCTION__;
return std::string_view (name.data () + a, b - a);
#elif defined(COMPILER_CLANG)
// std::string_view cruft::introspection::name::member() [TypeT = int, KlassT = foo, Ptr = &foo::a]
//
// The strategy is to count back from the end of the string to find
// the ampersand and return everything between that and the final
// character (which is always a ']').
constexpr std::size_t a = detail::rfind (__PRETTY_FUNCTION__, ':') + 1;
constexpr std::size_t b = detail::rfind (__PRETTY_FUNCTION__, ']');
constexpr std::string_view name = __PRETTY_FUNCTION__;
return std::string_view (name.data () + a, b - a);
#else
#error "Unsupported compiler"
#endif
}
template <auto Ptr>
constexpr std::string_view
member (void)
{
return member<
typename pointer_to_member_type <decltype(Ptr)>::type,
typename pointer_to_member_class<decltype(Ptr)>::type,
Ptr
> ();
}
constexpr std::size_t
member_offset (auto ptr)
{
using klass_t = typename pointer_to_member_class<decltype (ptr)>::type;
klass_t instance {};
return std::uintptr_t (&(instance.*ptr)) - std::uintptr_t (&instance);
}
///////////////////////////////////////////////////////////////////////////
template <typename T>
struct type_char;
template <> struct type_char<float > { static constexpr char value = 'f'; };
template <> struct type_char<double> { static constexpr char value = 'd'; };
template <> struct type_char< int8_t> { static constexpr char value = 'i'; };
template <> struct type_char< int16_t> { static constexpr char value = 'i'; };
template <> struct type_char< int32_t> { static constexpr char value = 'i'; };
template <> struct type_char< int64_t> { static constexpr char value = 'i'; };
template <> struct type_char< uint8_t> { static constexpr char value = 'u'; };
template <> struct type_char<uint16_t> { static constexpr char value = 'u'; };
template <> struct type_char<uint32_t> { static constexpr char value = 'u'; };
template <> struct type_char<uint64_t> { static constexpr char value = 'u'; };
template <typename T>
constexpr auto type_char_v = type_char<T>::value;
}