introspection: work around clang static constexpr arrays

This commit is contained in:
Danny Robson 2016-12-21 16:46:16 +11:00
parent 74f7bc2e1a
commit 9116404f30
2 changed files with 95 additions and 73 deletions

View File

@ -18,21 +18,23 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
constexpr const char* ::util::type_name<bool>::value; constexpr const char ::util::detail::type_name_bool::value[];
constexpr const char ::util::detail::type_name_char::value[];
constexpr const char ::util::detail::type_name_voidp::value[];
constexpr const char* ::util::type_name< int8_t>::value; constexpr const char ::util::detail::type_name_u08::value[];
constexpr const char* ::util::type_name< int16_t>::value; constexpr const char ::util::detail::type_name_u16::value[];
constexpr const char* ::util::type_name< int32_t>::value; constexpr const char ::util::detail::type_name_u32::value[];
constexpr const char* ::util::type_name< int64_t>::value; constexpr const char ::util::detail::type_name_u64::value[];
constexpr const char* ::util::type_name< uint8_t>::value; constexpr const char ::util::detail::type_name_s08::value[];
constexpr const char* ::util::type_name<uint16_t>::value; constexpr const char ::util::detail::type_name_s16::value[];
constexpr const char* ::util::type_name<uint32_t>::value; constexpr const char ::util::detail::type_name_s32::value[];
constexpr const char* ::util::type_name<uint64_t>::value; constexpr const char ::util::detail::type_name_s64::value[];
constexpr const char* ::util::type_name<float>::value; constexpr const char ::util::detail::type_name_f32::value[];
constexpr const char* ::util::type_name<double>::value; constexpr const char ::util::detail::type_name_f64::value[];
constexpr const char* ::util::type_name<std::string>::value; constexpr const char ::util::detail::type_name_string::value[];
constexpr const char* ::util::type_name<char*>::value; constexpr const char ::util::detail::type_name_cstring::value[];
constexpr const char* ::util::type_name<const char*>::value; constexpr const char ::util::detail::type_name_const_cstring::value[];

View File

@ -26,36 +26,48 @@
#include <tuple> #include <tuple>
namespace util { namespace util {
// XXX: we should be using a const char[] here, but clang-3.9 will not // XXX: clang-3.9/clang-4.0 will not instantiate static constexpr member
// instantiate array values within template specialisations. // variables from class specialisations, so we have to use detail classes
// to hold the variables and instantiate _those_ members instead.
template <typename T> template <typename T>
struct type_name; struct type_name;
template <> struct type_name<bool> { static constexpr const char *value = "bool"; }; #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 { };
template <> struct type_name<char> { static constexpr const char *value = "char"; }; CLANG_WORKAROUND(bool,bool,"bool")
template <> struct type_name<void*> { static constexpr const char *value = "void*"; }; CLANG_WORKAROUND(char,char,"char")
CLANG_WORKAROUND(void*,voidp,"void*")
template <> struct type_name< int8_t> { static constexpr const char *value = "int8"; }; CLANG_WORKAROUND( uint8_t, u08, "u08")
template <> struct type_name< int16_t> { static constexpr const char *value = "int16"; }; CLANG_WORKAROUND(uint16_t, u16, "u16")
template <> struct type_name< int32_t> { static constexpr const char *value = "int32"; }; CLANG_WORKAROUND(uint32_t, u32, "u32")
template <> struct type_name< int64_t> { static constexpr const char *value = "int64"; }; CLANG_WORKAROUND(uint64_t, u64, "u64")
template <> struct type_name< uint8_t> { static constexpr const char *value = "uint8"; }; CLANG_WORKAROUND( int8_t, s08, "s08")
template <> struct type_name<uint16_t> { static constexpr const char *value = "uint16"; }; CLANG_WORKAROUND(int16_t, s16, "s16")
template <> struct type_name<uint32_t> { static constexpr const char *value = "uint32"; }; CLANG_WORKAROUND(int32_t, s32, "s32")
template <> struct type_name<uint64_t> { static constexpr const char *value = "uint64"; }; CLANG_WORKAROUND(int64_t, s64, "s64")
template <> struct type_name<float > { static constexpr const char *value = "float32"; }; CLANG_WORKAROUND(float, f32, "f32")
template <> struct type_name<double > { static constexpr const char *value = "float64"; }; CLANG_WORKAROUND(double, f64, "f64")
template <> struct type_name<std::string> { static constexpr const char *value = "string"; }; CLANG_WORKAROUND(const char*, const_cstring, "cstring")
template <> struct type_name<char*> { static constexpr const char *value = "cstring"; }; CLANG_WORKAROUND(char*, cstring, "cstring")
template <> struct type_name<const char*> { static constexpr const char *value = "cstring"; }; CLANG_WORKAROUND(std::string, string, "string")
#undef CLANG_WORKAROUND
template <typename T> template <typename T>
constexpr constexpr
const char* type_name_v = type_name<T>::value; auto type_name_v = type_name<T>::value;
template <typename T> template <typename T>
const char* const char*
@ -67,69 +79,77 @@ namespace util {
/// Lists valid values of an enumeration /// Lists valid values of an enumeration
/// ///
/// E: enumeration type /// E: enumeration type
///
/// Specialisations must provide the following constexpr:
/// value_type: typename
/// value_count: size_t
/// values: static const std::array<value_type,value_count>
template < template <
typename E typename E
> >
struct enum_traits { struct enum_traits;
/// Specialisations must provide the following constexpr:
///
/// value_type: typename
/// value_count: size_t
/// values: static const std::array<value_type,value_count>
};
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/// Defines specialisations for introspection data structures for an /// Defines specialisations for introspection data structures for an
/// enum E, in namespace NS, with variadic values __VA_ARGS__. /// enum E, in namespace NS, with variadic values __VA_ARGS__.
/// ///
/// Expects to be caleld from outside all namespaces. /// Expects to be caleld from outside all namespaces.
///
/// XXX: If we define the constexpr fields in a template specialised class
/// clang has trouble instantiating them (with std=c++1z) resulting in
/// undefined symbols at link time. By using a simple struct and inheriting
/// from it we can avoid this problem. Revist this solution at clang-4.0.
#define INTROSPECTION_ENUM_DECL(NS,E, ...) \ #define INTROSPECTION_ENUM_DECL(NS,E, ...) \
namespace util { \ namespace util { \
template <> \ struct PASTE(__enum_traits_,E) { \
struct enum_traits<::NS::E> { \ using value_type = ::NS::E; \
using value_type = ::NS::E; \ \
\ static constexpr \
static constexpr \ size_t value_count = VA_ARGS_COUNT(__VA_ARGS__); \
size_t value_count = VA_ARGS_COUNT(__VA_ARGS__); \ \
\ static const \
static constexpr \ std::array<value_type,value_count> \
std::array<value_type,value_count> \ values; \
values = { \ \
MAP1(NAMESPACE_LIST, ::NS::E, __VA_ARGS__) \ static const \
}; \ std::array<const char*,value_count> \
\ names; \
static constexpr \ }; \
std::array<const char*,value_count> \ \
names = { MAP(STRINGIZE_LIST, __VA_ARGS__) }; \ template <> \
}; \ struct enum_traits<::NS::E> : public PASTE(__enum_traits_,E) \
\ { }; \
template <> \ \
struct type_name<::NS::E> { \ template <> \
static constexpr const char ns[] = #NS; \ struct type_name<::NS::E> { \
static constexpr const char value[] = #E; \ static constexpr const char ns[] = #NS; \
}; \ static constexpr const char value[] = #E; \
} \ }; \
} \
///------------------------------------------------------------------------ ///------------------------------------------------------------------------
/// Declares specialisations for introspection data structures for an /// Declares specialisations for introspection data structures for an
/// enum E, in namespace NS, with variadic values __VA_ARGS__. /// enum E, in namespace NS, with variadic values __VA_ARGS__.
/// ///
/// Expects to be caleld from outside all namespaces. /// Expects to be called from outside all namespaces.
#define INTROSPECTION_ENUM_IMPL(NS,E, ...) \ #define INTROSPECTION_ENUM_IMPL(NS,E, ...) \
constexpr \ constexpr \
std::array< \ std::array< \
util::enum_traits<::NS::E>::value_type, \ util::enum_traits<::NS::E>::value_type, \
util::enum_traits<::NS::E>::value_count \ util::enum_traits<::NS::E>::value_count \
> util::enum_traits<::NS::E>::values; \ > PASTE(util::__enum_traits_,E)::values = { \
MAP1(NAMESPACE_LIST, ::NS::E, __VA_ARGS__) \
}; \
\ \
constexpr \ const \
std::array< \ std::array< \
const char*, \ const char*, \
util::enum_traits<::NS::E>::value_count \ util::enum_traits<::NS::E>::value_count \
> util::enum_traits<::NS::E>::names; \ > PASTE(util::__enum_traits_,E)::names = { \
MAP(STRINGIZE_LIST, __VA_ARGS__) \
}; \
\ \
constexpr \ constexpr \
const char util::type_name<::NS::E>::ns[]; \ const char util::type_name<::NS::E>::ns[]; \
@ -224,7 +244,7 @@ namespace util {
/// Defines an enum, its values, associated introspection structures, and /// Defines an enum, its values, associated introspection structures, and
/// istream and ostream operators. /// istream and ostream operators.
/// ///
/// This must be called from outside all namespaces as /// This must be called from outside all namespaces as
/// INTROSPECTION_ENUM_DECL and INTROSPECTION_ENUM_IMPL need to declare /// INTROSPECTION_ENUM_DECL and INTROSPECTION_ENUM_IMPL need to declare
/// and define structures outside the user's namespace. /// and define structures outside the user's namespace.
@ -295,7 +315,7 @@ namespace util {
/// field<foo,int,&foo::b> /// field<foo,int,&foo::b>
/// > fields; /// > fields;
/// }; /// };
/// ///
/// template <> const std::string field<foo,int,&foo::a>::name = "a"; /// template <> const std::string field<foo,int,&foo::a>::name = "a";
/// template <> const std::string field<foo,int,&foo::b>::name = "b"; /// template <> const std::string field<foo,int,&foo::b>::name = "b";