/* * 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 #include namespace cruft::introspection { /////////////////////////////////////////////////////////////////////////// /// 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::introspection { \ 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::introspection::enum_traits<::NS::E>::value_type, \ cruft::introspection::enum_traits<::NS::E>::value_count \ > PASTE(cruft::introspection::__enum_traits_,E)::values = { \ MAP1(NAMESPACE_LIST, ::NS::E, __VA_ARGS__) \ }; \ \ const \ std::array< \ const char*, \ cruft::introspection::enum_traits<::NS::E>::value_count \ > PASTE(cruft::introspection::__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::introspection::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::introspection:;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::introspection::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) }