introspection: use __FUNCTION__ parsing method for type_name queries

This commit is contained in:
Danny Robson 2019-04-16 10:17:53 +10:00
parent 1cd107d27e
commit 71c44b3293
3 changed files with 92 additions and 60 deletions

View File

@ -137,7 +137,7 @@ namespace cruft::cmdopt {
{
static const std::string EXAMPLE =
std::string {"<"} +
std::string {to_string<T> ()} +
std::string {type_name<T> ()} +
std::string {">"};
return EXAMPLE;

View File

@ -6,74 +6,86 @@
* Copyright 2015-2017 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_INTROSPECTION_HPP
#define __UTIL_INTROSPECTION_HPP
#pragma once
#include "std.hpp"
#include "variadic.hpp"
#include <cruft/util/preprocessor.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <ostream>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
namespace cruft {
// clang#18781: clang-3.9/clang-4.0 will not instantiate static constexpr
// member variables from class specialisations, so we have to use detail
// classes to hold the variables and instantiate _those_ members instead.
template <typename T>
struct type_name;
#define CLANG_WORKAROUND(TYPE,TAG,NAME) \
namespace detail { \
struct type_name_##TAG { \
static constexpr const char value[] = (NAME); \
}; \
} \
\
template <> \
struct type_name<TYPE> : public detail::type_name_##TAG { };
CLANG_WORKAROUND(bool,bool,"bool")
CLANG_WORKAROUND(char,char,"char")
CLANG_WORKAROUND(void*,voidp,"void*")
CLANG_WORKAROUND( uint8_t, u08, "u08")
CLANG_WORKAROUND(uint16_t, u16, "u16")
CLANG_WORKAROUND(uint32_t, u32, "u32")
CLANG_WORKAROUND(uint64_t, u64, "u64")
CLANG_WORKAROUND( int8_t, s08, "i08")
CLANG_WORKAROUND(int16_t, s16, "i16")
CLANG_WORKAROUND(int32_t, s32, "i32")
CLANG_WORKAROUND(int64_t, s64, "i64")
CLANG_WORKAROUND(float, f32, "f32")
CLANG_WORKAROUND(double, f64, "f64")
CLANG_WORKAROUND(const char*, const_cstring, "cstring")
CLANG_WORKAROUND(char*, cstring, "cstring")
CLANG_WORKAROUND(std::string, string, "string")
CLANG_WORKAROUND(std::filesystem::path, path, "path");
CLANG_WORKAROUND(std::byte, byte, "byte");
#undef CLANG_WORKAROUND
template <typename T>
constexpr
auto type_name_v = type_name<T>::value;
std::string_view type_name (void)
{
std::string_view pretty_function (__PRETTY_FUNCTION__);
template <typename T>
const char*
to_string (void)
{ return type_name_v<T>; }
#ifdef __clang__
std::string_view const prefix = "[T = ";
#elif defined(__GNUC__)
std::string_view prefix = "[with T = ";
#else
#error "Unsupported compiler"
#endif
// Find the location where the type begins. It will also mark the start of
// the type's namespace if the type has one.
auto const ns_begin = std::search (
pretty_function.begin (),
pretty_function.end (),
prefix.begin (),
prefix.end ()
);
// Find the point the type name ends. Both use ']', but gcc lists
// std::string_view too and so requires the delimiting ';' as a suffix.
char const suffixes[] = ";]";
auto const type_end = std::find_first_of (
ns_begin,
pretty_function.end (),
std::begin (suffixes),
std::end (suffixes)
);
// Find the end of any namespace symbols
auto const ns_symbol = std::find (
std::make_reverse_iterator (type_end),
std::make_reverse_iterator (ns_begin),
':'
);
// Compute the start of the symbol's type and return a view over this.
auto const data = ns_symbol != std::make_reverse_iterator (ns_begin)
? &*ns_symbol + 1
: ns_begin + prefix.size ();
auto const size = std::distance (data, type_end);
return std::string_view (data, size);
}
template <> constexpr std::string_view type_name<i08> (void) { return "i08"; }
template <> constexpr std::string_view type_name<i16> (void) { return "i16"; }
template <> constexpr std::string_view type_name<i32> (void) { return "i32"; }
template <> constexpr std::string_view type_name<i64> (void) { return "i64"; }
template <> constexpr std::string_view type_name<u08> (void) { return "u08"; }
template <> constexpr std::string_view type_name<u16> (void) { return "u16"; }
template <> constexpr std::string_view type_name<u32> (void) { return "u32"; }
template <> constexpr std::string_view type_name<u64> (void) { return "u64"; }
template <> constexpr std::string_view type_name<f32> (void) { return "f32"; }
template <> constexpr std::string_view type_name<f64> (void) { return "f64"; }
///////////////////////////////////////////////////////////////////////////
@ -141,12 +153,6 @@ namespace cruft {
template <> \
struct enum_traits<::NS::E> : public PASTE(__enum_traits_,E) \
{ }; \
\
template <> \
struct type_name<::NS::E> { \
static constexpr const char ns[] = #NS; \
static constexpr const char value[] = #E; \
}; \
} \
@ -440,5 +446,3 @@ namespace cruft {
template <typename K>
auto as_tuple (K&&) = delete;
}
#endif

View File

@ -1,7 +1,11 @@
#include "introspection.hpp"
#include "tap.hpp"
#include "std.hpp"
//-----------------------------------------------------------------------------
#include "tap.hpp"
#include <iostream>
///////////////////////////////////////////////////////////////////////////////
// simple test struct of scalars
struct foo
{
@ -11,6 +15,14 @@ struct foo
//-----------------------------------------------------------------------------
namespace bar {
struct qux {
short c;
};
}
///////////////////////////////////////////////////////////////////////////////
// define introspection data
namespace cruft
{
@ -51,5 +63,21 @@ int main ()
"dynamic member access after conversion to tuple");
}
tap.expect_eq (cruft::view (cruft::type_name<i08> ()), "i08", "i08 type_name");
tap.expect_eq (cruft::view (cruft::type_name<i16> ()), "i16", "i16 type_name");
tap.expect_eq (cruft::view (cruft::type_name<i32> ()), "i32", "i32 type_name");
tap.expect_eq (cruft::view (cruft::type_name<i64> ()), "i64", "i64 type_name");
tap.expect_eq (cruft::view (cruft::type_name<u08> ()), "u08", "u08 type_name");
tap.expect_eq (cruft::view (cruft::type_name<u16> ()), "u16", "u16 type_name");
tap.expect_eq (cruft::view (cruft::type_name<u32> ()), "u32", "u32 type_name");
tap.expect_eq (cruft::view (cruft::type_name<u64> ()), "u64", "u64 type_name");
tap.expect_eq (cruft::view (cruft::type_name<f32> ()), "f32", "f32 type_name");
tap.expect_eq (cruft::view (cruft::type_name<f64> ()), "f64", "f64 type_name");
tap.expect_eq (cruft::view (cruft::type_name<foo> ()), "foo", "struct type_name");
tap.expect_eq (cruft::view (cruft::type_name<bar::qux> ()), "qux", "namespaced struct type_name");
return tap.status ();
}