mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-06 16:56:42 +08:00
Simple implementation of N4428 enum reflection.
This commit is contained in:
parent
b182e16ec3
commit
f1a0b5d0e4
74
doc/demo/105-c++17-reflection.md
Normal file
74
doc/demo/105-c++17-reflection.md
Normal file
@ -0,0 +1,74 @@
|
||||
## C++17 reflection
|
||||
|
||||
Better Enums can be used to approximately implement the enums portion of the
|
||||
[$cxx17 reflection proposal N4428][n4428] in $cxx11. The implementation is
|
||||
*approximate* in the following senses:
|
||||
|
||||
- It only applies to Better Enums, not built-in enums.
|
||||
- `enum_traits<E>::enumerators::get<I>::identifier` is a non-`constexpr`
|
||||
function rather than a `constexpr` variable. I could make it a `constexpr`
|
||||
variable as in the proposal, but that requires
|
||||
[compile-time name trimming][slow-enum] to be enabled for the Better Enum
|
||||
on which `get` is used. Since that's an opt-in feature, I can't guarantee it.
|
||||
I preferred not to write feature-detection code, in order to keep the
|
||||
implementation simple.
|
||||
- The return type of `identifier` is `const char*` instead of the proposed
|
||||
`std::string_literal`, because I don't have an implementation of the latter
|
||||
available. I'm also ignoring the requirements on encoding, and just taking
|
||||
whatever the preprocessor provides.
|
||||
|
||||
With that out of the way, we can look at a simple example.
|
||||
|
||||
[n4428]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4428.pdf
|
||||
[slow-enum]: ${prefix}OptInFeatures.html#CompileTimeNameTrimming
|
||||
|
||||
---
|
||||
|
||||
The implementation is defined in [`extra/better-enums/n4428.h`][header]. Let's
|
||||
assume that `extra/` has been added as a directory to search for include files.
|
||||
|
||||
#include <iostream>
|
||||
<em>#include</em> <<em>enum.h</em>>
|
||||
<em>#include</em> <<em>better-enums/n4428.h</em>>
|
||||
|
||||
[header]: https://github.com/aantron/better-enums/blob/$ref/extra/better-enums/n4428.h
|
||||
|
||||
---
|
||||
|
||||
Let's declare an enum:
|
||||
|
||||
<em>ENUM</em>(<em>Channel</em>, <em>char</em>, <em>Red</em> = <em>1</em>, <em>Green</em>, <em>Blue</em>)
|
||||
|
||||
N4428 proposes three `constexpr` traits, of which we have two implemented
|
||||
exactly — that is, as `constexpr`:
|
||||
|
||||
<em>constexpr std::size_t size</em> =
|
||||
<em>std</em>::<em>enum_traits</em><<em>Channel</em>>::<em>enumerators</em>::<em>size</em>;
|
||||
|
||||
<em>constexpr Channel value_0</em> =
|
||||
<em>std</em>::<em>enum_traits</em><<em>Channel</em>>::<em>enumerators</em>::<em>get</em><<em>0</em>>::<em>value</em>;
|
||||
<em>constexpr Channel value_1</em> =
|
||||
<em>std</em>::<em>enum_traits</em><<em>Channel</em>>::<em>enumerators</em>::<em>get</em><<em>1</em>>::<em>value</em>;
|
||||
|
||||
Let's check the results:
|
||||
|
||||
static_assert(<em>size</em> == <em>3</em>, "");
|
||||
|
||||
static_assert(<em>value_0</em> == +<em>Channel::Red</em>, "");
|
||||
static_assert(<em>value_1</em> == +<em>Channel::Green</em>, "");
|
||||
|
||||
Finally, we can try using `identifier`:
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout
|
||||
<< <em>std</em>::<em>enum_traits</em><<em>Channel</em>>::<em>enumerators</em>::<em>get</em><<em>2</em>>::<em>identifier</em>()
|
||||
<< std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
That prints `Blue`, as you would expect.
|
||||
|
||||
%% description = Approximate implementation of N4428 enum reflection based on
|
||||
Better Enums.
|
||||
1
doc/template/cxx17.tmpl
vendored
Normal file
1
doc/template/cxx17.tmpl
vendored
Normal file
@ -0,0 +1 @@
|
||||
<span class="cpp">C++</span><span class="eleven">17</span>
|
||||
2
doc/template/ref.tmpl
vendored
2
doc/template/ref.tmpl
vendored
@ -1 +1 @@
|
||||
0.10.1
|
||||
master
|
||||
62
example/105-c++17-reflection.cc
Normal file
62
example/105-c++17-reflection.cc
Normal file
@ -0,0 +1,62 @@
|
||||
// This file was generated automatically.
|
||||
|
||||
// C++17 reflection
|
||||
//
|
||||
// Better Enums can be used to approximately implement the enums portion of the
|
||||
// C++17 reflection proposal N4428 in C++11. The implementation is approximate
|
||||
// in the following senses:
|
||||
//
|
||||
// 1. It only applies to Better Enums, not built-in enums.
|
||||
// 2. enum_traits<E>::enumerators::get<I>::identifier is a non-constexpr
|
||||
// function rather than a constexpr variable. I could make it a constexpr
|
||||
// variable as in the proposal, but that requires compile-time name
|
||||
// trimming to be enabled for the Better Enum on which get is used. Since
|
||||
// that's an opt-in feature, I can't guarantee it. I preferred not to write
|
||||
// feature-detection code, in order to keep the implementation simple.
|
||||
// 3. The return type of identifier is const char* instead of the proposed
|
||||
// std::string_literal, because I don't have an implementation of the
|
||||
// latter available. I'm also ignoring the requirements on encoding, and
|
||||
// just taking whatever the preprocessor provides.
|
||||
//
|
||||
// With that out of the way, we can look at an example.
|
||||
|
||||
// The implementation is defined in extra/better-enums/n4428.h. Let's assume
|
||||
// that extra/ has been added as an include file path.
|
||||
|
||||
#include <iostream>
|
||||
#include <enum.h>
|
||||
#include <better-enums/n4428.h>
|
||||
|
||||
|
||||
// Let's declare an enum:
|
||||
|
||||
ENUM(Channel, char, Red = 1, Green, Blue)
|
||||
|
||||
// N4428 requires three constexpr traits, of which we have two implemented
|
||||
// exactly, that is, as constexpr:
|
||||
|
||||
constexpr std::size_t size =
|
||||
std::enum_traits<Channel>::enumerators::size;
|
||||
|
||||
constexpr Channel value_0 =
|
||||
std::enum_traits<Channel>::enumerators::get<0>::value;
|
||||
constexpr Channel value_1 =
|
||||
std::enum_traits<Channel>::enumerators::get<1>::value;
|
||||
|
||||
// Let's check the results:
|
||||
|
||||
static_assert(size == 3, "");
|
||||
|
||||
static_assert(value_0 == +Channel::Red, "");
|
||||
static_assert(value_1 == +Channel::Green, "");
|
||||
|
||||
// Finally, we can try using identifier:
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout
|
||||
<< std::enum_traits<Channel>::enumerators::get<2>::identifier()
|
||||
<< std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
46
extra/better-enums/n4428.h
Normal file
46
extra/better-enums/n4428.h
Normal file
@ -0,0 +1,46 @@
|
||||
// This file is part of Better Enums, released under the BSD 2-clause license.
|
||||
// See doc/LICENSE for details, or visit http://github.com/aantron/better-enums.
|
||||
|
||||
// This file provides an implementation of the enum reflection interface
|
||||
// proposed in
|
||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4428.pdf
|
||||
// over Better Enums.
|
||||
|
||||
// See further discussion and a demonstration of usage at
|
||||
// example/107-c++17-reflection.cc, or visit:
|
||||
// http://aantron.github.io/better-enums/demo/C++17Reflection.html
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef BETTER_ENUMS__N4428_H
|
||||
#define BETTER_ENUMS__N4428_H
|
||||
|
||||
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename Enum>
|
||||
struct enum_traits {
|
||||
struct enumerators {
|
||||
constexpr static const size_t size = Enum::_size();
|
||||
|
||||
template <size_t Index>
|
||||
struct get {
|
||||
constexpr static Enum value = Enum::_values()[Index];
|
||||
|
||||
// In the proposal, this is a constexpr variable of type
|
||||
// string_literal. It is possible to declare "identifier" that way
|
||||
// with Better Enums, but only for enums that have compile-time name
|
||||
// trimming enabled. Since compile-time name trimming currently has
|
||||
// a severe performance impact, and is disabled by default, I chose
|
||||
// to implement "identifier" as a function.
|
||||
static const char* identifier() { return Enum::_names()[Index]; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // #ifndef BETTER_ENUMS__N4428_H
|
||||
@ -89,10 +89,11 @@ endforeach(TEST)
|
||||
set(EXAMPLES
|
||||
1-hello-world 2-conversions 3-iterate 4-switch 5-iostreams 6-safety
|
||||
7-representation 8-constexpr 101-special-values 102-any-underlying
|
||||
103-bitset 104-quine)
|
||||
103-bitset 104-quine 105-c++17-reflection)
|
||||
|
||||
set(SKIPPED_FOR_CXX98
|
||||
8-constexpr 101-special-values 102-any-underlying 103-bitset 104-quine)
|
||||
8-constexpr 101-special-values 102-any-underlying 103-bitset 104-quine
|
||||
105-c++17-reflection)
|
||||
|
||||
set(SKIPPED_FOR_STRICT_CONVERSION 4-switch 102-any-underlying)
|
||||
|
||||
@ -112,6 +113,7 @@ endforeach(EXAMPLE)
|
||||
# Add compiler flags.
|
||||
|
||||
include_directories(..)
|
||||
include_directories(../extra)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
1
test/expect/105-c++17-reflection
Normal file
1
test/expect/105-c++17-reflection
Normal file
@ -0,0 +1 @@
|
||||
Blue
|
||||
Loading…
x
Reference in New Issue
Block a user