diff --git a/cmdopt.hpp b/cmdopt.hpp index b77aca80..abc31f5b 100644 --- a/cmdopt.hpp +++ b/cmdopt.hpp @@ -137,7 +137,7 @@ namespace cruft::cmdopt { { static const std::string EXAMPLE = std::string {"<"} + - std::string {to_string ()} + + std::string {type_name ()} + std::string {">"}; return EXAMPLE; diff --git a/introspection.hpp b/introspection.hpp index baca34b6..41da873e 100644 --- a/introspection.hpp +++ b/introspection.hpp @@ -6,74 +6,86 @@ * Copyright 2015-2017 Danny Robson */ -#ifndef __UTIL_INTROSPECTION_HPP -#define __UTIL_INTROSPECTION_HPP +#pragma once +#include "std.hpp" #include "variadic.hpp" #include +#include #include #include #include #include #include #include +#include #include #include 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 - 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 : 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 constexpr - auto type_name_v = type_name::value; + std::string_view type_name (void) + { + std::string_view pretty_function (__PRETTY_FUNCTION__); - template - const char* - to_string (void) - { return type_name_v; } +#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 (void) { return "i08"; } + template <> constexpr std::string_view type_name (void) { return "i16"; } + template <> constexpr std::string_view type_name (void) { return "i32"; } + template <> constexpr std::string_view type_name (void) { return "i64"; } + + template <> constexpr std::string_view type_name (void) { return "u08"; } + template <> constexpr std::string_view type_name (void) { return "u16"; } + template <> constexpr std::string_view type_name (void) { return "u32"; } + template <> constexpr std::string_view type_name (void) { return "u64"; } + + template <> constexpr std::string_view type_name (void) { return "f32"; } + template <> constexpr std::string_view type_name (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 auto as_tuple (K&&) = delete; } - -#endif diff --git a/test/introspection.cpp b/test/introspection.cpp index 5f1fde50..8f25b5df 100644 --- a/test/introspection.cpp +++ b/test/introspection.cpp @@ -1,7 +1,11 @@ #include "introspection.hpp" -#include "tap.hpp" +#include "std.hpp" -//----------------------------------------------------------------------------- +#include "tap.hpp" +#include + + +/////////////////////////////////////////////////////////////////////////////// // 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 type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "i16", "i16 type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "i32", "i32 type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "i64", "i64 type_name"); + + tap.expect_eq (cruft::view (cruft::type_name ()), "u08", "u08 type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "u16", "u16 type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "u32", "u32 type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "u64", "u64 type_name"); + + tap.expect_eq (cruft::view (cruft::type_name ()), "f32", "f32 type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "f64", "f64 type_name"); + + tap.expect_eq (cruft::view (cruft::type_name ()), "foo", "struct type_name"); + tap.expect_eq (cruft::view (cruft::type_name ()), "qux", "namespaced struct type_name"); + return tap.status (); }