From 9a2389cd15c05bafa8bebf8d3a194d9c789e34cc Mon Sep 17 00:00:00 2001 From: Anton Bachin Date: Sun, 4 Oct 2015 18:27:56 -0500 Subject: [PATCH] Eliminated non-integral underlying types. This was an experimental feature. Removing it to simplify maintenance. --- README.md | 2 - doc/ApiReference.md | 139 ------------------------- doc/DesignDecisionsFAQ.md | 8 -- doc/GeneralUnderlyingTypes.md | 117 --------------------- doc/demo/102-any-underlying.md | 172 ------------------------------- doc/index.md | 8 -- enum.h | 161 ++++------------------------- example/102-any-underlying.cc | 164 ------------------------------ test/CMakeLists.txt | 12 +-- test/cxxtest/underlying.h | 180 --------------------------------- 10 files changed, 27 insertions(+), 936 deletions(-) delete mode 100644 doc/GeneralUnderlyingTypes.md delete mode 100644 doc/demo/102-any-underlying.md delete mode 100644 example/102-any-underlying.cc delete mode 100644 test/cxxtest/underlying.h diff --git a/README.md b/README.md index a0c1b78..b765ef6 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,9 @@ the rich enums that are missing from standard C++. - Stream operators. - Does not use the heap and can be compiled with exceptions disabled, for use in minimal freestanding environments. -- The underlying type [does not have to be an integral type][underlying]. [testing]: http://aantron.github.io/better-enums/CompilerSupport.html [performance]: http://aantron.github.io/better-enums/Performance.html -[underlying]: http://aantron.github.io/better-enums/demo/NonIntegralUnderlyingTypes.html ## Limitations diff --git a/doc/ApiReference.md b/doc/ApiReference.md index 09bade4..ce035af 100644 --- a/doc/ApiReference.md +++ b/doc/ApiReference.md @@ -51,15 +51,6 @@ passed by value. All names declared in the scope of a Better Enum are prefixed with an underscore in order to avoid conflicts with potential constant names. -If you are using [non-integral underlying types][non-integral], you need to be -aware of section of this reference on underlying types. However, if you are -using a regular, integral underlying type, the type `Enum::_underlying` is the -same `Enum::_integral`, and each of the `*_underlying` functions is the same as -the corresponding `*_integral` function, so you can safely ignore that whole -section. - -[non-integral]: ${prefix}demo/NonIntegralUnderlyingTypes.html - ### Running example @@ -394,136 +385,6 @@ as [`_from_string`](#_from_string). In case of failure, sets the stream's -### Non-integral underlying type - -This section is relevant only if you are using an underlying type that is not -an integral type — otherwise, `Enum::_underlying` is the same as -`Enum::_integral`, and all the functions described here are redundant with their -corresponding functions in the [section on integer conversions][integral]. - -That section is written for the simple, but common case where the underlying -type is an integral type, in order to avoid overloading users not using the -feature described here with unnecessary generality. The information in that -section is fully accurate for integral underlying types, but for non-integral -underlying types this section corrects it. - -[integral]: #IntegerConversion - -The rest of this section will assume that your non-integral underlying type is -called `Underlying`. - -In this case, the memory representation of your Better Enum type is the same as -for `Underlying`. In fact, this is always true — the memory representation -is always the same as for the underlying type. It is only a matter of whether -that type is integral or not. - -When `Underlying` is not integral, Better Enums still needs an integral -representation of `Underlying` for use in `switch`. That is the true meaning of -the member type `_integral`. It's just that when `Underlying` *is* integral to -begin with, it is its own integral representation, and the two types collapse. - -To support non-integral underlying types, Better Enums requires a two-way -mapping between `Underlying` and some type `_integral`. In case `Underlying` -*is* integral, however, that mapping is simply the identity function. Otherwise, -you have to supply a mapping as shown [here][non-integral]. - -In short, the underlying type is "first-class," whether it is integral or not, -and the type `_integral` is a helper type. When `Underlying` *is* integral, the -various `*_integral` functions just happen to work with the underlying type, as -a special case. The material in this section is for the general case where -`Underlying` is not integral. - -#### typename _underlying - -`Enum::_underlying` is the same type as `Underlying`. It has to satisfy the -requirements given [here][non-integral]. - -#### non-member specialization struct better_enums::integral_mapping<Underlying> - -You should specialize this template for `Underlying`, as shown in the -[example][non-integral]. The specialization needs the following members: - -- A type `integral_representation`, which gives an integral type that Better - Enums will use to make `Underlying` compatible with `switch` statements, and - to define an ordering on the generated Better Enums type. This type is *not* - the internal representation of the Better Enum — the Better Enum's - memory representation is the same as `Underlying`. -- A function - `constexpr static integral_representation to_integral(const Underlying&)`. -- A function - `constexpr static Underlying from_integral(integral_representation)`. - -In $cxx98, the above functions don't have to be `constexpr`. - -You can avoid specializing this template, but its default implementation puts -additional requirements on `Underlying` in order to be able to define default -versions of `to_integral` and `from_integral`: - -- `Underlying` must have a member type `integral_representation`, with the same - meaning as above. -- `Underlying` must have a conversion - `constexpr operator integral_representation() const`. -- `Underlying` must have a constructor - `constexpr Underlying(integral_representation)`. This constructor can be - explicit. - -Again, in $cxx98, these members don't have to be `constexpr`. - -#### member constexpr _underlying _to_underlying() const - -No-op conversion of a Better Enum to its underlying type. Behaves as -[`_to_integral`](#_to_integral), except that the text concerning implicit -conversions is irrelevant when `_underlying` is not the same as `_integral`. -Implicit conversions, if not disabled, are always to `_integral`. - -#### static constexpr Enum _from_underlying(_underlying) - -Same as [`_from_integral`](#_from_integral), but for the underlying type. In -fact, `from_integral` is a wrapper that first converts the integer to a value of -the underlying type (a no-op when the types are equal), and then calls -`_from_underlying`. - -#### static constexpr optional _from_underlying_nothrow(_underlying) - -Same as [`_from_integral_nothrow`](#_from_integral_nothrow), but for the -underlying type. `_from_integral_nothrow` is a wrapper as described -[above](#_from_underlying). - -#### static constexpr Enum _from_underlying_unchecked(_underlying) - -Same as [`_from_integral_unchecked`](#_from_integral), but for the underlying -type. `_from_integral_unchecked` is a wrapper as described -[above](#_from_underlying). - -#### static constexpr bool _is_valid(_underlying) - -Replaces [`_is_valid(_integral)`](#_is_valid_integral). In fact, *this* function -is the only one defined, but in the case where `_integral` is `_underlying`, -this function's signature is equivalent to -[`_is_valid(_integral)`](#_is_valid_integral). - -#### static constexpr _value_iterable _values — _underlying[]() - -Collection of declared enum values, stored in memory as instances of the -underlying type. - -Replaces [`_values`](#_values), the collection of integral values of declared -constants. In fact, this is the only member defined — in the case where -`_integral` is the same as `_underlying`, the definition in the section on -integer conversions is equivalent to this one. - -#### member constexpr const _underlying& operator *() const - -Returns a reference to the wrapped underlying value. There is also a non-`const` -version. - -#### member constexpr const _underlying* operator ->() const - -Returns a pointer to the wrapped underlying value that is suitable for member -access, if `_underlying` has members. - - - %% class = api %% description = Detailed description of the Better Enums API. diff --git a/doc/DesignDecisionsFAQ.md b/doc/DesignDecisionsFAQ.md index 84808e3..eb04895 100644 --- a/doc/DesignDecisionsFAQ.md +++ b/doc/DesignDecisionsFAQ.md @@ -186,13 +186,6 @@ However, it also introduces some difficulties. type, similar to how it is done [here][infer], but that will not be suitable for all contexts, and the user may be surprised by ambiguous resolution error messages when it is not. -- The experimental feature presented [here][underlying] would be questionable in - the traits interpretation. It is still possible to have a non-integral - underlying type with traits, but it would be strange if the traits version of - the macro accepted a non-integral underlying type, and then declared the - actual language enum with an integral underlying type. Even though that is - exactly what the non-traits version does, there the language enum is hidden - inside the generated type, instead of being exposed alongside a traits type. - Scoped constants are lost for $cxx98 unless Better Enums again wraps them in a generated type, though it will be more lightweight than a full Better Enum of the non-traits approach. @@ -272,7 +265,6 @@ Choosing this approach has serious drawbacks. [traits]: #Traits [implicit]: ${prefix}OptInFeatures.html#StrictConversions [infer]: ${prefix}demo/SpecialValues.html -[underlying]: ${prefix}demo/NonIntegralUnderlyingTypes.html [traits-branch]: $repo/tree/traits [traits-samples]: $repo/tree/traits/samples diff --git a/doc/GeneralUnderlyingTypes.md b/doc/GeneralUnderlyingTypes.md deleted file mode 100644 index 9aa4e96..0000000 --- a/doc/GeneralUnderlyingTypes.md +++ /dev/null @@ -1,117 +0,0 @@ -## General underlying types - -The underlying type of a Better Enum doesn't have to be an integral type. It can -be any literal type `T`, as long as you provide a `constexpr` two-way mapping -between `T` and an integral type of your choosing. It also works in $cxx98, -though, of course, `T` doesn't have to be literal and the mapping doesn't have -to be `constexpr` — everything will be done by Better Enums at run time. - -Doing this enables the following usage: - - // The type. A color triplet. - struct html_color { - uint8_t r, g, b; - - constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) : - r(_r), g(_g), b(_b) { } - }; - - - - // The enum. - BETTER_ENUM(Color, html_color, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954) - - - - // The usage. - Color c = Color::darksalmon; - - std::cout << "Red component: " << c->r << std::endl; - - switch (c) { - case Color::darksalmon: // ... - case Color::purplemimosa: // ... - case Color::slimegreen: // ... - } - -As you can see, you can have an enumerated set of any literal type, and safely -use the values in `switch`, with the compiler checking exhaustiveness. You can -also access the type's members using the `enum->underlying_member` syntax. - -You do have to supply the mapping to an integral type, however. One option is: - - // The mapping. It just stuffs bits. - template <> - struct ::better_enums::underlying_traits { - using integral_representation = unsigned int; - - constexpr static html_color from_integral(unsigned int i) - { return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); } - - constexpr static unsigned int to_integral(html_color c) - { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; } - }; - -### Using constructors in initializers - -The declaration above used only numeric initializers. It is possible to use the -type's own constructors, provided the type has a `constexpr` conversion to your -chosen integral type: - - // The type. - struct html_color { - uint8_t r, g, b; - - constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) : - r(_r), g(_g), b(_b) { } - - // This is new: - constexpr operator unsigned int() const - { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; } - }; - - // The enum. - BETTER_ENUM(Color, html_color, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954, - celeste = html_color(0x50, 0xeb, 0xec)) - -This is not possible at all in $cxx98, however. - -### Letting the compiler enumerate your literal type - -You don't have to use initializers. For example, as long as your example type -`file_descriptor` knows how to deal with the values, you can have the compiler -generate them in sequence: - - BETTER_ENUM(FD, file_descriptor, - STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...) - -SAMPLE - -You can see the code "in action" in the [test case][test]. Be aware that it's -not very "nice," because it uses conditional compilation to run under multiple -compilers. I haven't written a clean sample or documentation yet simply because -this feature is in a very early stage of development. - -[test]: $repo/blob/master/test/cxxtest/underlying.h - -### Discussion - -This feature is still semi-experimental, though I expect it to remain stable, -except perhaps that I will make it possible to infer the type -`integral_representation`. - -Any opinions are welcome. - -- The main reason Better Enums needs you to supply and explicit mapping is - because it can't just get the "bits" of objects of underlying type in - `constexpr` code. Both `reinterpret_cast` and union abuse seem to be forbidden - in `constexpr` functions. -- There is currently no way to have two different integral representaitons for - the same underlying type in different enums. I don't think that's a major use - case at this point, however. - -%% description = "Using Better Enums with non-integral underlying types." diff --git a/doc/demo/102-any-underlying.md b/doc/demo/102-any-underlying.md deleted file mode 100644 index 3ea68c6..0000000 --- a/doc/demo/102-any-underlying.md +++ /dev/null @@ -1,172 +0,0 @@ -## Non-integral underlying types - -The underlying type of a Better Enum doesn't have to be an integral type. It can -be any literal type `T`, as long as you provide a `constexpr` two-way mapping -between `T` and an integral type of your choosing. This also works in $cxx98 -— though then, of course, `T` doesn't have to be literal and the mapping -doesn't have to be `constexpr`. In $cxx98, everything involving `T` will simply -be done by Better Enums at run time. - -This feature is semi-experimental. I am considering relaxing the requirements on -`T` so that it doesn't have to be literal. I can use a `reinterpret_cast` to -make a mapping automatically. This will make non-integral underlying types -easier to use, but will also prevent usage at compile time, which unfortunately -has structural consequences for the implementation of Better Enums, and -additional semantic consequences for usage, even at run time. - -In the meantime, here's how to have a non-integral underlying type in the -current version. - - #include - #include - typedef unsigned char uint8_t; // not in C++98. - - - - // The underlying type. A color triplet. - struct html_color { - uint8_t r, g, b; - - constexpr html_color(uint8_t _r, uint8_t _g, uint8_t _b) : - r(_r), g(_g), b(_b) { } - }; - - // The mapping. It just stuffs bits to get the same effect as - // reinterpret_cast, except reinterpret_cast is not available in constexpr - // functions, so we have to write the bit manipulations out. On modern - // C++11 compilers, you don't have to enter the better_enums namespace like - // this - you can just do - // struct ::better_enums::integral_mapping { ... - namespace better_enums { - - template <> - struct integral_mapping { - using integral_representation = unsigned int; - - constexpr static html_color from_integral(unsigned int i) - { return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); } - - constexpr static unsigned int to_integral(html_color c) - { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; } - }; - - } - - - - // The enum itself. - BETTER_ENUM(Color, html_color, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954) - -Now, we can do: - - int main() - { - Color color = Color::darksalmon; - - std::cout << std::hex; - std::cout << "Red component: " << (int)color->r << std::endl; - std::cout << "Green component: " << (int)color->g << std::endl; - std::cout << "Blue component: " << (int)color->b << std::endl; - - std::cout << color._to_string() << std::endl; - - switch (color) { - case Color::darksalmon: return 0; - case Color::purplemimosa: return 1; - case Color::slimegreen: return 2; - } - - return 0; - } - -This prints each component, the name of the color (`"darksalmon"`), and then -exits from the `switch` with status 0. - -### Constructors in initializers - -The above declaration used only numbers in initializers, but it is actually -possible to use constructors of `html_color`. We have to add a `constexpr` -converting operator directly to `html_color`, however: - -~~~comment -struct better_html_color { - uint8_t r, g, b; - - constexpr better_html_color(uint8_t _r, uint8_t _g, uint8_t _b) : - r(_r), g(_g), b(_b) { } - - // This is new: - constexpr operator unsigned int() const - { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; } -}; - -namespace better_enums { - -template <> -struct integral_mapping { - using integral_representation = unsigned int; - - constexpr static better_html_color from_integral(unsigned int i) - { - return better_html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); - } - - constexpr static unsigned int to_integral(better_html_color c) - { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; } -}; - -} -~~~ - -This allows: - -~~~comment -BETTER_ENUM(BetterColor, better_html_color, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954, - celeste = better_html_color(0x50, 0xeb, 0xec)) -~~~ - -If you can't edit your literal type to add this converting operator, or don't -want to for type safety reasons, you can achieve a similar effect by declaring -an intermediate type `U` that `html_color` can convert to, that can convert to -the integral type. Then, cast your constructor call to `U`. The type `U` is for -declarations only. - -Constructors in initializers require $cxx11. Also, g++ doesn't support this -before 5.1. - -### Letting the compiler enumerate your type - -Of course, as long as the values are valid, you can let the compiler enumerate -your type as in a regular enum, by omitting initializers: - -~~~comment -BETTER_ENUM(FD, file_descriptor, - STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...) -~~~ - -Here, `FD::STDIN` maps to the integral representation 0, `STDOUT` to 1, and so -on. - -### Discussion - -This feature is still semi-experimental, though I expect it to remain stable, -except perhaps that I will make it possible to infer the type -`integral_representation`. - -Any opinions are welcome. - -- The main reason Better Enums needs you to supply and explicit mapping is - because it can't just get the "bits" of objects of underlying type in - `constexpr` code. Both `reinterpret_cast` and union abuse seem to be forbidden - in `constexpr` functions. -- There is currently no way to have two different integral representaitons for - the same underlying type in different enums. I don't think that's a major use - case at this point, however. - - - -%% description = "Using Better Enums with non-integral underlying types." diff --git a/doc/index.md b/doc/index.md index 3c3d68c..01f671d 100644 --- a/doc/index.md +++ b/doc/index.md @@ -163,14 +163,6 @@ Try it live online in
  • - Non-integral underlying types - - Have sets of named, switch-friendly constants of any literal - type. - -
  • - -
  • Free and open source Released under the BSD license for use in any project, free or commercial. diff --git a/enum.h b/enum.h index 66856f4..0ba3ad4 100644 --- a/enum.h +++ b/enum.h @@ -25,21 +25,11 @@ # if !defined(__EXCEPTIONS) || !__has_feature(cxx_exceptions) # define BETTER_ENUMS__NO_EXCEPTIONS # endif -# if __cplusplus >= 201103L -# define BETTER_ENUMS__HAVE_LONG_LONG -# define BETTER_ENUMS__HAVE_NEW_CHAR_TYPES -# endif # else # if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) # define BETTER_ENUMS__HAVE_CONSTEXPR # endif -# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) -# define BETTER_ENUMS__HAVE_LONG_LONG -# endif -# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) -# define BETTER_ENUMS__HAVE_NEW_CHAR_TYPES -# endif # endif # ifndef __EXCEPTIONS # define BETTER_ENUMS__NO_EXCEPTIONS @@ -48,9 +38,6 @@ #endif #ifdef _MSC_VER -# if _MSC_VER >= 1600 -# define BETTER_ENUMS__HAVE_LONG_LONG -# endif # ifndef _CPPUNWIND # define BETTER_ENUMS__NO_EXCEPTIONS # endif @@ -484,56 +471,6 @@ inline void _trim_names(const char * const *raw_names, -// General underlying types. - -// This template is unfortunately necessary (?) due to the lack of -// in C++98. , this template could be replaced with a -// combination of std::conditional and std::is_integral. -template -struct _representation { typedef typename T::integral_representation type; }; - -template <> struct _representation { typedef bool type; }; -template <> struct _representation { typedef char type; }; -template <> struct _representation { typedef wchar_t type; }; -template <> struct _representation { typedef signed char type; }; -template <> struct _representation - { typedef unsigned char type; }; -template <> struct _representation { typedef short type; }; -template <> struct _representation - { typedef unsigned short type; }; -template <> struct _representation { typedef int type; }; -template <> struct _representation { typedef unsigned int type; }; -template <> struct _representation { typedef long type; }; -template <> struct _representation - { typedef unsigned long type; }; - -#ifdef BETTER_ENUMS__HAVE_LONG_LONG - -template <> struct _representation { typedef long long type; }; -template <> struct _representation - { typedef unsigned long long type; }; - -#endif - -#ifdef BETTER_ENUMS__HAVE_NEW_CHAR_TYPES - -template <> struct _representation { typedef char16_t type; }; -template <> struct _representation { typedef char32_t type; }; - -#endif - -template -struct integral_mapping { - typedef typename _representation::type integral_representation; - - BETTER_ENUMS__CONSTEXPR static integral_representation - to_integral(const T &v) { return (integral_representation)v; } - BETTER_ENUMS__CONSTEXPR static T - from_integral(integral_representation n) { return T(n); } -}; - - - // Eager initialization. template struct _initialize_at_program_start { @@ -618,13 +555,6 @@ constexpr const char *_final_ ## index = \ // The enums proper. -// TODO Convert integral to underlying only at the last possible moment, if the -// integral value is valid. This should prevent unexpected behavior (conversions -// during scans). - -// TODO Choose the right return type semantics once the _values array -// representation is chosen: values if integral, const references if underlying. - #define BETTER_ENUMS__NS(EnumType) better_enums::_data_ ## EnumType #ifdef BETTER_ENUMS__VC2008_WORKAROUNDS @@ -656,45 +586,21 @@ class Enum { \ private: \ typedef ::better_enums::optional _optional; \ typedef ::better_enums::optional _optional_index; \ - typedef ::better_enums::integral_mapping _mapping; \ \ public: \ - typedef Underlying _underlying; \ - typedef _mapping::integral_representation _integral; \ + typedef Underlying _integral; \ \ enum _enumerated SetUnderlyingType(Underlying) { __VA_ARGS__ }; \ \ - BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : \ - _value(_mapping::from_integral(value)) { } \ + BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : _value(value) { } \ \ BETTER_ENUMS__COPY_CONSTRUCTOR(Enum) \ \ BETTER_ENUMS__CONSTEXPR operator SwitchType(Enum)() const \ { \ - return (SwitchType(Enum))_mapping::to_integral(_value); \ + return SwitchType(Enum)(_value); \ } \ \ - _underlying& operator *() { return _value; } \ - BETTER_ENUMS__CONSTEXPR const _underlying& operator *() const \ - { return _value; } \ - \ - _underlying* operator ->() { return &_value; } \ - BETTER_ENUMS__CONSTEXPR const _underlying* operator ->() const \ - { return &_value; } \ - \ - _underlying _to_underlying() { return _value; } \ - BETTER_ENUMS__CONSTEXPR const _underlying& _to_underlying() const \ - { return _value; } \ - \ - BETTER_ENUMS__IF_EXCEPTIONS( \ - BETTER_ENUMS__CONSTEXPR static Enum \ - _from_underlying(const _underlying &value); \ - ) \ - BETTER_ENUMS__CONSTEXPR static Enum \ - _from_underlying_unchecked(const _underlying &value); \ - BETTER_ENUMS__CONSTEXPR static _optional \ - _from_underlying_nothrow(const _underlying &value); \ - \ BETTER_ENUMS__CONSTEXPR _integral _to_integral() const; \ BETTER_ENUMS__IF_EXCEPTIONS( \ BETTER_ENUMS__CONSTEXPR static Enum _from_integral(_integral value); \ @@ -717,7 +623,7 @@ class Enum { \ BETTER_ENUMS__CONSTEXPR static _optional \ _from_string_nocase_nothrow(const char *name); \ \ - BETTER_ENUMS__CONSTEXPR static bool _is_valid(const _underlying &value); \ + BETTER_ENUMS__CONSTEXPR static bool _is_valid(_integral value); \ BETTER_ENUMS__CONSTEXPR static bool _is_valid(const char *name); \ BETTER_ENUMS__CONSTEXPR static bool _is_valid_nocase(const char *name); \ \ @@ -736,18 +642,18 @@ class Enum { \ BETTER_ENUMS__CONSTEXPR static _value_iterable _values(); \ ToStringConstexpr static _name_iterable _names(); \ \ - _underlying _value; \ + _integral _value; \ \ private: \ - Enum() : _value(_mapping::from_integral(0)) { } \ + Enum() : _value(0) { } \ \ - explicit BETTER_ENUMS__CONSTEXPR Enum(const _underlying &value) : \ + explicit BETTER_ENUMS__CONSTEXPR Enum(const _integral &value) : \ _value(value) { } \ \ DeclareInitialize \ \ BETTER_ENUMS__CONSTEXPR static _optional_index \ - _from_value_loop(const _underlying &value, std::size_t index = 0); \ + _from_value_loop(_integral value, std::size_t index = 0); \ BETTER_ENUMS__CONSTEXPR static _optional_index \ _from_string_loop(const char *name, std::size_t index = 0); \ BETTER_ENUMS__CONSTEXPR static _optional_index \ @@ -778,34 +684,9 @@ operator +(Enum::_enumerated enumerated) \ return (Enum)enumerated; \ } \ \ -BETTER_ENUMS__IF_EXCEPTIONS( \ -BETTER_ENUMS__CONSTEXPR inline Enum \ -Enum::_from_underlying(const _underlying &value) \ -{ \ - return \ - ::better_enums::_or_throw( \ - _from_underlying_nothrow(value), \ - #Enum "::_from_underlying: invalid argument"); \ -} \ -) \ - \ -BETTER_ENUMS__CONSTEXPR inline Enum \ -Enum::_from_underlying_unchecked(const _underlying &value) \ -{ \ - return Enum(value); \ -} \ - \ -BETTER_ENUMS__CONSTEXPR inline Enum::_optional \ -Enum::_from_underlying_nothrow(const _underlying &value) \ -{ \ - return \ - ::better_enums::_map_index(BETTER_ENUMS__NS(Enum)::_value_array, \ - _from_value_loop(value)); \ -} \ - \ BETTER_ENUMS__CONSTEXPR inline Enum::_integral Enum::_to_integral() const \ { \ - return _mapping::to_integral(_value); \ + return _integral(_value); \ } \ \ BETTER_ENUMS__CONSTEXPR inline Enum \ @@ -826,7 +707,9 @@ BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_integral(_integral value) \ BETTER_ENUMS__CONSTEXPR inline Enum::_optional \ Enum::_from_integral_nothrow(_integral value) \ { \ - return _from_underlying_nothrow(_mapping::from_integral(value)); \ + return \ + ::better_enums::_map_index(BETTER_ENUMS__NS(Enum)::_value_array, \ + _from_value_loop(value)); \ } \ \ ToStringConstexpr inline const char* Enum::_to_string() const \ @@ -873,7 +756,7 @@ Enum::_from_string_nocase_nothrow(const char *name) \ _from_string_nocase_loop(name)); \ } \ \ -BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(const _underlying &value) \ +BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(_integral value) \ { \ return _from_value_loop(value); \ } \ @@ -908,14 +791,14 @@ ToStringConstexpr inline Enum::_name_iterable Enum::_names() \ DefineInitialize(Enum) \ \ BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \ -Enum::_from_value_loop(const Enum::_underlying &value, std::size_t index) \ +Enum::_from_value_loop(Enum::_integral value, std::size_t index) \ { \ return \ - index == _size() ? _optional_index() : \ - _mapping::to_integral( \ - BETTER_ENUMS__NS(Enum)::_value_array[index]._value) \ - == _mapping::to_integral(value) ? _optional_index(index) : \ - _from_value_loop(value, index + 1); \ + index == _size() ? \ + _optional_index() : \ + BETTER_ENUMS__NS(Enum)::_value_array[index]._value == value ? \ + _optional_index(index) : \ + _from_value_loop(value, index + 1); \ } \ \ BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \ @@ -962,7 +845,7 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \ // C++11 #define BETTER_ENUMS__CXX11_UNDERLYING_TYPE(Underlying) \ - : ::better_enums::integral_mapping::integral_representation + : Underlying // C++98, C++11 #define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE(Type) \ @@ -977,9 +860,7 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \ // C++11 #define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...) \ - enum class _EnumClassForSwitchStatements : \ - ::better_enums::integral_mapping::integral_representation \ - { __VA_ARGS__ }; + enum class _EnumClassForSwitchStatements : Underlying { __VA_ARGS__ }; // C++98 #define BETTER_ENUMS__CXX98_TRIM_STRINGS_ARRAYS(Enum, ...) \ diff --git a/example/102-any-underlying.cc b/example/102-any-underlying.cc deleted file mode 100644 index ffe4d5c..0000000 --- a/example/102-any-underlying.cc +++ /dev/null @@ -1,164 +0,0 @@ -// This file was generated automatically. - -// Non-integral underlying types -// -// The underlying type of a Better Enum doesn't have to be an integral type. It -// can be any literal type T, as long as you provide a constexpr two-way mapping -// between T and an integral type of your choosing. This also works in C++98 - -// though then, of course, T doesn't have to be literal and the mapping doesn't -// have to be constexpr. In C++98, everything involving T will simply be done by -// Better Enums at run time. -// -// This feature is semi-experimental. I am considering relaxing the requirements -// on T so that it doesn't have to be literal. I can use a reinterpret_cast to -// make a mapping automatically. This will make non-integral underlying types -// easier to use, but will also prevent usage at compile time, which -// unfortunately has structural consequences for the implementation of Better -// Enums, and additional semantic consequences for usage, even at run time. -// -// In the meantime, here's how to have a non-integral underlying type in the -// current version. - -#include -#include -typedef unsigned char uint8_t; // not in C++98. - - - -// The underlying type. A color triplet. -struct html_color { - uint8_t r, g, b; - - constexpr html_color(uint8_t _r, uint8_t _g, uint8_t _b) : - r(_r), g(_g), b(_b) { } -}; - -// The mapping. It just stuffs bits to get the same effect as -// reinterpret_cast, except reinterpret_cast is not available in constexpr -// functions, so we have to write the bit manipulations out. On modern -// C++11 compilers, you don't have to enter the better_enums namespace like -// this - you can just do -// struct ::better_enums::integral_mapping { ... -namespace better_enums { - -template <> -struct integral_mapping { - using integral_representation = unsigned int; - - constexpr static html_color from_integral(unsigned int i) - { return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); } - - constexpr static unsigned int to_integral(html_color c) - { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; } -}; - -} - - - -// The enum itself. -BETTER_ENUM(Color, html_color, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954) - -// Now, we can do: - -int main() -{ - Color color = Color::darksalmon; - - std::cout << std::hex; - std::cout << "Red component: " << (int)color->r << std::endl; - std::cout << "Green component: " << (int)color->g << std::endl; - std::cout << "Blue component: " << (int)color->b << std::endl; - - std::cout << color._to_string() << std::endl; - - switch (color) { - case Color::darksalmon: return 0; - case Color::purplemimosa: return 1; - case Color::slimegreen: return 2; - } - - return 0; -} - -// This prints each component, the name of the color ("darksalmon"), and then -// exits from the switch with status 0. -// -// Constructors in initializers -// -// The above declaration used only numbers in initializers, but it is actually -// possible to use constructors of html_color. We have to add a constexpr -// converting operator directly to html_color, however: -// -// struct better_html_color { -// uint8_t r, g, b; -// -// constexpr better_html_color(uint8_t _r, uint8_t _g, uint8_t _b) : -// r(_r), g(_g), b(_b) { } -// -// // This is new: -// constexpr operator unsigned int() const -// { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; } -// }; -// -// namespace better_enums { -// -// template <> -// struct integral_mapping { -// using integral_representation = unsigned int; -// -// constexpr static better_html_color from_integral(unsigned int i) -// { -// return better_html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); -// } -// -// constexpr static unsigned int to_integral(better_html_color c) -// { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; } -// }; -// -// } -// -// This allows: -// -// BETTER_ENUM(BetterColor, better_html_color, -// darksalmon = 0xc47451, purplemimosa = 0x9e7bff, -// slimegreen = 0xbce954, -// celeste = better_html_color(0x50, 0xeb, 0xec)) -// -// If you can't edit your literal type to add this converting operator, or don't -// want to for type safety reasons, you can achieve a similar effect by -// declaring an intermediate type U that html_color can convert to, that can -// convert to the integral type. Then, cast your constructor call to U. The type -// U is for declarations only. -// -// Constructors in initializers require C++11. Also, g++ doesn't support this -// before 5.1. -// -// Letting the compiler enumerate your type -// -// Of course, as long as the values are valid, you can let the compiler -// enumerate your type as in a regular enum, by omitting initializers: -// -// BETTER_ENUM(FD, file_descriptor, -// STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...) -// -// Here, FD::STDIN maps to the integral representation 0, STDOUT to 1, and so -// on. -// -// Discussion -// -// This feature is still semi-experimental, though I expect it to remain stable, -// except perhaps that I will make it possible to infer the type -// integral_representation. -// -// Any opinions are welcome. -// -// 1. The main reason Better Enums needs you to supply and explicit mapping is -// because it can't just get the "bits" of objects of underlying type in -// constexpr code. Both reinterpret_cast and union abuse seem to be -// forbidden in constexpr functions. -// 2. There is currently no way to have two different integral representaitons -// for the same underlying type in different enums. I don't think that's a -// major use case at this point, however. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2565222..74b6350 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -112,14 +112,14 @@ endforeach(TEST) set(EXAMPLES 1-hello-world 2-conversions 3-iterate 4-switch 6-iostreams 7-safety - 8-representation 9-constexpr 101-special-values 102-any-underlying - 103-bitset 104-quine 105-c++17-reflection) - -set(SKIPPED_FOR_CXX98 - 5-map 9-constexpr 101-special-values 102-any-underlying 103-bitset 104-quine + 8-representation 9-constexpr 101-special-values 103-bitset 104-quine 105-c++17-reflection) -set(SKIPPED_FOR_STRICT_CONVERSION 4-switch 102-any-underlying) +set(SKIPPED_FOR_CXX98 + 5-map 9-constexpr 101-special-values 103-bitset 104-quine + 105-c++17-reflection) + +set(SKIPPED_FOR_STRICT_CONVERSION 4-switch) if(CONFIGURATION STREQUAL CXX98 OR NOT SUPPORTS_CONSTEXPR) list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_CXX98}) diff --git a/test/cxxtest/underlying.h b/test/cxxtest/underlying.h deleted file mode 100644 index 900d4a6..0000000 --- a/test/cxxtest/underlying.h +++ /dev/null @@ -1,180 +0,0 @@ -#include -#include - -// This test uses internal constants declared by feature testing macros in -// enum.h. They are not part of the public interface, but are used here to avoid -// duplication of definitions for C++11 and C++98. -// -// BETTER_ENUMS__CONSTEXPR is a synonym for constexpr when available, and -// othewise expands to nothing. - - - -// Using a type trait. -struct html_color_1 { - unsigned char r, g, b; - - BETTER_ENUMS__CONSTEXPR - html_color_1(unsigned char _r, unsigned char _g, unsigned char _b) : - r(_r), g(_g), b(_b) { } -}; - -// In C++11, can simply use struct ::better_enums::integral_mapping below, -// instead of having to enclose the specialization in the namespace. -namespace better_enums { - -template <> -struct integral_mapping { - typedef unsigned int integral_representation; - - BETTER_ENUMS__CONSTEXPR static html_color_1 from_integral(unsigned int i) - { return html_color_1(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); } - - BETTER_ENUMS__CONSTEXPR static unsigned int to_integral(html_color_1 v) - { return (unsigned int)v.r << 16 | (unsigned int)v.g << 8 | v.b; } -}; - -} - -BETTER_ENUM(HtmlColor1, html_color_1, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954) - - - -// Using member definitions. -struct html_color_2 { - unsigned char r, g, b; - - BETTER_ENUMS__CONSTEXPR - html_color_2(unsigned char _r, unsigned char _g, unsigned char _b) : - r(_r), g(_g), b(_b) { } - - - - typedef unsigned int integral_representation; - - explicit BETTER_ENUMS__CONSTEXPR html_color_2(unsigned int i) : - r(i >> 16 & 0xff), g(i >> 8 & 0xff), b(i & 0xff) { } - - BETTER_ENUMS__CONSTEXPR operator unsigned int() const - { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; } -}; - -// Initializers are not supported before GCC 5.1, even with a literal type and -// constexpr conversion to integer. -#ifdef BETTER_ENUMS__HAVE_CONSTEXPR -# ifdef __GNUC__ -# ifdef __clang__ -# define SUPPORT_INITIALIZER -# else -# if __GNUC__ >= 5 -# define SUPPORT_INITIALIZER -# endif -# endif -# endif -#endif - -#ifdef SUPPORT_INITIALIZER -BETTER_ENUM(HtmlColor2, html_color_2, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954, celeste = html_color_2(0x50, 0xeb, 0xec)) -#else -BETTER_ENUM(HtmlColor2, html_color_2, - darksalmon = 0xc47451, purplemimosa = 0x9e7bff, - slimegreen = 0xbce954) -#endif - - - -class UnderlyingTypeTests : public CxxTest::TestSuite { - public: - void test_basics_and_operators() - { - HtmlColor1 color_1 = HtmlColor1::darksalmon; - - TS_ASSERT_EQUALS(color_1->r, 0xc4); - TS_ASSERT_EQUALS(color_1->g, 0x74); - TS_ASSERT_EQUALS(color_1->b, 0x51); - - TS_ASSERT_EQUALS(sizeof(HtmlColor1), sizeof(html_color_1)); - - HtmlColor2 color_2 = HtmlColor2::purplemimosa; - - TS_ASSERT_EQUALS(color_2->r, 0x9e); - TS_ASSERT_EQUALS(color_2->g, 0x7b); - TS_ASSERT_EQUALS(color_2->b, 0xff); - - TS_ASSERT_EQUALS(sizeof(HtmlColor2), sizeof(html_color_2)); - -#ifdef SUPPORT_INITIALIZER - HtmlColor2 color_3 = HtmlColor2::celeste; - - TS_ASSERT_EQUALS(color_3->r, 0x50); - TS_ASSERT_EQUALS(color_3->g, 0xeb); - TS_ASSERT_EQUALS(color_3->b, 0xec); -#endif - - color_1->r = 0; - - const HtmlColor1 const_color = HtmlColor1::slimegreen; - TS_ASSERT_EQUALS(const_color->r, 0xbc); - - html_color_1 &color_1_reference = *color_1; - const html_color_1 &const_color_reference = *const_color; - - TS_ASSERT_EQUALS(color_1_reference.r, 0); - TS_ASSERT_EQUALS(const_color_reference.r, 0xbc); - } - - void test_conversion_functions() - { - HtmlColor2 color_1 = HtmlColor2::darksalmon; - html_color_2 color_1_underlying = color_1._to_underlying(); - - TS_ASSERT_EQUALS(color_1_underlying.r, color_1->r); - TS_ASSERT_EQUALS(color_1_underlying.g, color_1->g); - TS_ASSERT_EQUALS(color_1_underlying.b, color_1->b); - - color_1_underlying = html_color_2(HtmlColor2::slimegreen); - color_1 = HtmlColor2::_from_underlying(color_1_underlying); - - TS_ASSERT_EQUALS(color_1->r, color_1_underlying.r); - TS_ASSERT_EQUALS(color_1->g, color_1_underlying.g); - TS_ASSERT_EQUALS(color_1->b, color_1_underlying.b); - - color_1_underlying = html_color_2(1, 2, 3); - color_1 = HtmlColor2::_from_underlying_unchecked(color_1_underlying); - - TS_ASSERT_EQUALS(color_1->r, color_1_underlying.r); - TS_ASSERT_EQUALS(color_1->g, color_1_underlying.g); - TS_ASSERT_EQUALS(color_1->b, color_1_underlying.b); - } - - void test_switch() - { -#ifndef BETTER_ENUMS_STRICT_CONVERSION - - HtmlColor1 html_color_1 = HtmlColor1::purplemimosa; - - switch (html_color_1) { - case HtmlColor1::darksalmon: TS_ASSERT(false); break; - case HtmlColor1::purplemimosa: TS_ASSERT(true); break; - case HtmlColor1::slimegreen: TS_ASSERT(false); break; - } - -#else -# ifdef BETTER_ENUMS__HAVE_CONSTEXPR - - HtmlColor1 html_color_1 = HtmlColor1::purplemimosa; - - switch (html_color_1) { - case +HtmlColor1::darksalmon: TS_ASSERT(false); break; - case +HtmlColor1::purplemimosa: TS_ASSERT(true); break; - case +HtmlColor1::slimegreen: TS_ASSERT(false); break; - } - -# endif -#endif - } -};