205 lines
11 KiB
C++
205 lines
11 KiB
C++
/*
|
|
* 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 <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <cruft/util/preprocessor.hpp>
|
|
|
|
|
|
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<value_type,value_count>
|
|
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<value_type,value_count> \
|
|
values; \
|
|
\
|
|
static const \
|
|
std::array<const char*,value_count> \
|
|
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<NS::E>
|
|
/// 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<NS::E> 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)
|
|
} |