/* * 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 2015-2017 Danny Robson */ #pragma once #include "std.hpp" #include "algo/search.hpp" #include #include #include #include #include #include #include #include #include #include namespace cruft { template constexpr std::string_view type_name (void) { std::string_view pretty_function (__PRETTY_FUNCTION__); #ifdef __clang__ // PRETTY_FUNCTION = std::string_view cruft::type_name() [T = std::__1::vector >] std::string_view const prefix = "[T = "; #elif defined(__GNUC__) // PRETTY_FUNCTION = "constexpr std::string_view cruft::type_name() [with T = std::__debug::vector; std::string_view = std::basic_string_view]" 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 start of the first template parameter (or the end of the // string otherwise), and the end of the template parameters for this // type. // // This cursor will be used to limit the scope of the namespace removal // we're about to do. auto const first_template = std::find (ns_begin, type_end, '<'); auto const end_first_template = cruft::search::balanced (first_template, type_end, '<', '>'); // Find the end of any namespace symbols auto const ns_symbol = std::find ( std::make_reverse_iterator (first_template), 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, end_first_template); 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"; } /////////////////////////////////////////////////////////////////////////// template struct type_char; template <> struct type_char { static constexpr char value = 'f'; }; template <> struct type_char { 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 { static constexpr char value = 'u'; }; template <> struct type_char { static constexpr char value = 'u'; }; template <> struct type_char { static constexpr char value = 'u'; }; template constexpr auto type_char_v = type_char::value; /////////////////////////////////////////////////////////////////////////// /// Lists valid values of an enumeration /// /// E: enumeration type /// /// Specialisations must provide the following constexpr: /// value_type: typename /// value_count: size_t /// values: static const std::array template < typename E > struct enum_traits; /////////////////////////////////////////////////////////////////////////// /// Defines specialisations for introspection data structures for an /// enum E, in namespace NS, with variadic values __VA_ARGS__. /// /// 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, ...) \ namespace cruft { \ struct PASTE(__enum_traits_,E) { \ using value_type = ::NS::E; \ \ static constexpr \ size_t value_count = VA_ARGS_COUNT(__VA_ARGS__); \ \ static const \ std::array \ values; \ \ static const \ std::array \ names; \ }; \ \ template <> \ struct enum_traits<::NS::E> : public PASTE(__enum_traits_,E) \ { }; \ } \ ///------------------------------------------------------------------------ /// Declares specialisations for introspection data structures for an /// enum E, in namespace NS, with variadic values __VA_ARGS__. /// /// Expects to be called from outside all namespaces. #define INTROSPECTION_ENUM_IMPL(NS,E, ...) \ const \ std::array< \ cruft::enum_traits<::NS::E>::value_type, \ cruft::enum_traits<::NS::E>::value_count \ > PASTE(cruft::__enum_traits_,E)::values = { \ MAP1(NAMESPACE_LIST, ::NS::E, __VA_ARGS__) \ }; \ \ const \ std::array< \ const char*, \ cruft::enum_traits<::NS::E>::value_count \ > PASTE(cruft::__enum_traits_,E)::names = { \ MAP0(STRINGIZE_LIST, __VA_ARGS__) \ }; ///------------------------------------------------------------------------ /// Defines an istream extraction operator for an enumeration E, within /// namespace NS /// /// Expects to be called from outside all namespaces. /// /// The user is responsible for specialising the ::cruft::enum_traits /// parameters which are used to drive the implementation (eg, through /// INTROSPECTION_ENUM_DECL, and INTROSPECTION_ENUM_IMPL). /// /// For trivial enumerations INTROSPECTION_ENUM may be easier to use. #define INTROSPECTION_ENUM_ISTREAM(NS,E) \ std::istream& \ ::NS::operator>> (std::istream &is, ::NS::E &e) \ { \ using traits = ::cruft::enum_traits<::NS::E>; \ \ std::string name; \ is >> name; \ \ std::transform (std::begin (name), \ std::end (name), \ std::begin (name), \ ::toupper); \ \ auto name_pos = std::find ( \ std::cbegin (traits::names), \ std::cend (traits::names), \ name \ ); \ \ if (name_pos == std::cend (traits::names)) { \ is.setstate (std::istream::failbit); \ } else { \ auto d = std::distance ( \ std::begin (traits::names), \ name_pos \ ); \ \ e = traits::values[d]; \ } \ \ return is; \ } /// Defines an ostream insertion operator for an enumeration E, within /// namespace NS. /// /// Expects to be called from outside all namespaces. /// /// The user is responsible for specialising the ::cruft::enum_traits /// parameters which are used to drive the implementation (eg, through /// INTROSPECTION_ENUM_DECL, and INTROSPECTION_ENUM_IMPL). /// /// For trivial enumerations INTROSPECTION_ENUM may be easier to use. #define INTROSPECTION_ENUM_OSTREAM(NS,E) \ std::ostream& \ ::NS::operator<< (std::ostream &os, ::NS::E e) \ { \ using traits = ::cruft::enum_traits<::NS::E>; \ \ auto val_pos = std::find ( \ std::cbegin (traits::values), \ std::cend (traits::values), \ e \ ); \ \ if (val_pos == std::cend (traits::values)) { \ os.setstate (std::ostream::failbit); \ } else { \ auto d = std::distance ( \ std::cbegin (traits::values), \ val_pos \ ); \ \ os << traits::names[d]; \ } \ \ return os; \ } /// Defines an enum, its values, associated introspection structures, and /// istream and ostream operators. /// /// This must be called from outside all namespaces as /// INTROSPECTION_ENUM_DECL and INTROSPECTION_ENUM_IMPL need to declare /// and define structures outside the user's namespace. /// /// The enum will be defined inside an inline namespace to simplify the /// passing of parameters to functions which require some namespace /// prefixing. This shouldn't have a practical effect on user code. #define INTROSPECTION_ENUM(E, ...) \ inline namespace detail_intr_enum { \ enum E { __VA_ARGS__ }; \ std::ostream& operator<< (std::ostream&, E); \ std::istream& operator>> (std::istream&, E&); \ } \ INTROSPECTION_ENUM_DECL(detail_intr_enum,E,__VA_ARGS__) \ INTROSPECTION_ENUM_IMPL(detail_intr_enum,E,__VA_ARGS__) \ INTROSPECTION_ENUM_ISTREAM(detail_intr_enum,E) \ INTROSPECTION_ENUM_OSTREAM(detail_intr_enum,E) #define INTROSPECTION_ENUM_CLASS(E, ...) \ inline namespace detail_intr_enum { \ enum class E { __VA_ARGS__ }; \ std::ostream& operator<< (std::ostream&, E); \ std::istream& operator>> (std::istream&, E&); \ } \ INTROSPECTION_ENUM_DECL(detail_intr_enum,E,__VA_ARGS__) \ INTROSPECTION_ENUM_IMPL(detail_intr_enum,E,__VA_ARGS__) \ INTROSPECTION_ENUM_ISTREAM(detail_intr_enum,E) \ INTROSPECTION_ENUM_OSTREAM(detail_intr_enum,E) /////////////////////////////////////////////////////////////////////////// /// Describes a single member variable in a type availabe for introspection /// /// K: target class /// R: member type /// M: pointer-to-member template < class K, typename R, R K::*M > struct field { typedef K klass; typedef R type; static const std::string name; static const R& get (const K &k) { return k.*M; } static R& get ( K &k) { return k.*M; } static R& get ( K &&) = delete; }; /////////////////////////////////////////////////////////////////////////// /// Holds the fields of a type available for introspection /// /// Specialise the following type struct with a 'fields' tuple of the /// members that should be accessed like below: /// /// struct foo { int a; int b; }; /// /// template <> struct type /// { /// typedef std::tuple< /// field, /// field /// > fields; /// }; /// /// template <> const std::string field::name = "a"; /// template <> const std::string field::name = "b"; template struct type { }; /////////////////////////////////////////////////////////////////////////// /// traits class which converts an introspected type to a tuple /// /// K: target class template struct type_tuple; template < typename ...T > struct type_tuple< std::tuple > { typedef std::tuple type; }; template < typename K, typename I = typename std::make_index_sequence< std::tuple_size< typename type::fields >::value > > struct _type_tuple; template < typename K, size_t ...I > struct _type_tuple < K, std::index_sequence > { typedef std::tuple< typename std::tuple_element< I, typename type::fields >::type::type... > type; }; template < typename K > struct type_tuple { typedef typename _type_tuple::type type; }; /////////////////////////////////////////////////////////////////////////// namespace detail { template < typename K, typename I = typename std::make_index_sequence< std::tuple_size< typename type::fields >::value > > struct _as_tuple; template < typename K, size_t ...I > struct _as_tuple < K, std::index_sequence > { static typename type_tuple::type make (const K &k) { return std::make_tuple ( std::tuple_element::fields>::type::get (k)... ); } static auto make (K&&) = delete; }; } /// Convert an introspection capable class instance into a tuple instance /// /// K: source class template auto as_tuple (const K &k) { return detail::_as_tuple::make (k); } template auto as_tuple (K &_k) { const K &k = _k; return as_tuple (k); } template auto as_tuple (K&&) = delete; }