diff --git a/introspection.hpp b/introspection.hpp index f174b5bb..396ee9cc 100644 --- a/introspection.hpp +++ b/introspection.hpp @@ -17,7 +17,8 @@ #ifndef __UTIL_INTROSPECTION_HPP #define __UTIL_INTROSPECTION_HPP -#include "variadic.hpp" +#include "./preprocessor.hpp" +#include "./variadic.hpp" #include #include @@ -60,7 +61,7 @@ namespace util { template < typename E > - struct enum_values { + struct enum_traits { /// Specialisations must provide the following constexpr: /// /// value_type: typename @@ -68,6 +69,167 @@ namespace util { /// values: static const std::array }; + /////////////////////////////////////////////////////////////////////////// + /// 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. + + #define INTROSPECTION_ENUM_DECL(NS,E, ...) \ + namespace util { \ + template <> \ + struct enum_traits { \ + using value_type = NS::E; \ + \ + static constexpr \ + size_t value_count = param_count (__VA_ARGS__); \ + \ + static constexpr \ + std::array \ + values = { __VA_ARGS__ }; \ + \ + static constexpr \ + std::array \ + names = { MAP(STRINGIZE_LIST, __VA_ARGS__) }; \ + }; \ + \ + template <> \ + struct type_string { \ + static constexpr const char value[] = #E; \ + }; \ + } \ + + + ///------------------------------------------------------------------------ + /// Declares 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. + + #define INTROSPECTION_ENUM_IMPL(NS,E, ...) \ + constexpr \ + std::array< \ + util::enum_traits::value_type, \ + util::enum_traits::value_count \ + > util::enum_traits::values; \ + \ + constexpr \ + std::array< \ + const char*, \ + util::enum_traits::value_count \ + > util::enum_traits::names; \ + \ + constexpr \ + const char util::type_string::value[]; \ + + + ///------------------------------------------------------------------------ + /// 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 ::util::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 = util::enum_traits; \ + \ + 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 ::util::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 = ::util::enum_traits; \ + \ + 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) + + /////////////////////////////////////////////////////////////////////////// /// Describes a single member variable in a type availabe for introspection ///