Skip to content

Commit eb7de86

Browse files
committed
Add enum and flag_enum type metafunctions - initial checkin
`enum` and `flag_enum` are implemented entirely using type metafunctions; they are not special hardwired language features, each is just a subset of the universe of general C++ classes. An `enum` is a `value`, and a `flag_enum` additionally has bitwise operations to set/test the flags in the usual way. To specify each enumerator, just declare it as a member - the `:` is not required if you don't want to specify the underlying type or the enumerator's value. It will default to `public static constexpr`. > Note: This is using the other recent [checkin that generalizes object aliases](9d86794) to allow `var :== value;` and `var: some_type = value;` to write `static constexpr` objects. Handy for enumerators. The underlying type defaults to the minimum-sized signed integer that can represent the values, one of `i8`, `i16`, `i32`, or `i64`. To explicitly specify the underlying type, name it on the first member (only). For example: `color: @enum type = { red: u16 = 0; green; blue; }`. Enums larger than 64 bits are not currently supported. When an enumerator's value is not specified, it defaults to the next consecutive value for `enum` and the next power-of-two value for `flag_enum`. TODO: `is value` is working for `enum` but not yet for `flag_enum` Pasting new feature test case `pure2-enum.cpp2`: ``` @@ -0,0 +1,81 @@ skat_game: @enum type = { diamonds := 9; hearts; // 10 spades; // 11 clubs; // 12 grand := 20; null := 23; } rgb: @enum type = { red; // 0 green; // 1 blue; // 2 } file_attributes: @flag_enum type = { cached; // 1 current; // 2 obsolete; // 4 } main: () = { // x : skat_game = 9; // error, can't construct skat_game from integer x := skat_game::clubs; // if x == 9 { } // error, can't compare skat_game and integer // if x == rgb::red { } // error, can't compare skat_game and rgb color std::cout << "with if else: "; if x == skat_game::diamonds { // ok, can compare two skat_games std::cout << "diamonds"; } else if skat_game::hearts == x { // ok, in either order std::cout << "hearts"; } else if x is (skat_game::spades) { // ok, using is std::cout << "spades"; } else if skat_game::clubs is (x) { // ok, using is std::cout << "clubs"; } else { std::cout << "not a suit"; } std::cout << "\nwith inspect: " << inspect x -> std::string { is (skat_game::diamonds) = "diamonds"; is (skat_game::hearts ) = "hearts"; is (skat_game::spades ) = "spades"; is (skat_game::clubs ) = "clubs"; is _ = "not a suit"; } << std::endl; // x = 9; // error, can't assign skat_game from integer // x = rgb::red; // error, can't assign skat_game from rgb color x = skat_game::diamonds; // ok, can assign one skat_game from another f := file_attributes::current | file_attributes::cached; f &= file_attributes::cached | file_attributes::obsolete; f |= file_attributes::current; f2 := file_attributes::cached; std::cout << "f as int is (f as int )$\n"; std::cout << "f2 as int is (f2 as int )$\n"; std::cout << "f == f2 is (f == f2 )$\n"; // The following are still in progress // std::cout << "f is (f2) is (f is (f2))$\n"; // std::cout << "f2 is (f ) is (f2 is (f ))$\n"; // if f is (file_attributes::cached ) { std::cout << "cached "; } // if f is (file_attributes::current ) { std::cout << "current "; } // if f is (file_attributes::obsolete) { std::cout << "obsolete "; } // if f is (file_attributes::none ) { std::cout << "none "; } // std::cout << "\n"; } ```
1 parent 9d86794 commit eb7de86

18 files changed

+755
-163
lines changed

include/cpp2util.h

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -860,15 +860,20 @@ auto is( X const& x ) -> bool {
860860
//
861861
inline constexpr auto is( auto const& x, auto const& value ) -> bool
862862
{
863+
// Value with customized operator_is case
864+
if constexpr (requires{ x.op_is(value); }) {
865+
return x.op_is(value);
866+
}
867+
863868
// Predicate case
864-
if constexpr (requires{ bool{ value(x) }; }) {
869+
else if constexpr (requires{ bool{ value(x) }; }) {
865870
return value(x);
866871
}
867872
else if constexpr (std::is_function_v<decltype(value)> || requires{ &value.operator(); }) {
868873
return false;
869874
}
870875

871-
// Value case
876+
// Value equality case
872877
else if constexpr (requires{ bool{x == value}; }) {
873878
return x == value;
874879
}
@@ -1534,6 +1539,19 @@ template<typename T>
15341539
using alien_memory = T volatile;
15351540

15361541

1542+
//-----------------------------------------------------------------------
1543+
//
1544+
// An implementation of GSL's narrow_cast with a clearly 'unsafe' name
1545+
//
1546+
//-----------------------------------------------------------------------
1547+
//
1548+
template <typename C, typename X>
1549+
constexpr auto unsafe_narrow( X&& x ) noexcept -> decltype(auto)
1550+
{
1551+
return static_cast<C>(CPP2_FORWARD(x));
1552+
}
1553+
1554+
15371555
//-----------------------------------------------------------------------
15381556
//
15391557
// strict_value: a strong typedef-like helper for value types
@@ -1550,18 +1568,30 @@ using alien_memory = T volatile;
15501568
//
15511569
template <typename T, typename Tag>
15521570
class strict_value {
1553-
T t;
1571+
T t = {};
15541572
public:
1555-
template <typename U> requires std::is_same_v<T,U>
1556-
explicit constexpr strict_value(U const& u) : t{u} { }
1573+
explicit constexpr strict_value() { }
15571574

1558-
template <typename U> requires std::is_same_v<T,U>
1575+
template <typename U>
1576+
explicit constexpr strict_value(U const& u) : t{unsafe_narrow<T>(u)} { }
1577+
1578+
template <typename U> requires std::is_convertible_v<T,U>
15591579
explicit constexpr operator U() const { return t; }
15601580

1561-
template <typename U> requires std::is_same_v<T,U>
1581+
template <typename U> requires std::is_convertible_v<T,U>
15621582
explicit constexpr operator U() { return t; }
15631583

15641584
auto operator<=>( strict_value const& ) const -> std::strong_ordering = default;
1585+
1586+
auto operator|=( strict_value const& that ) -> strict_value { t |= that.t; return *this; }
1587+
auto operator&=( strict_value const& that ) -> strict_value { t &= that.t; return *this; }
1588+
auto operator^=( strict_value const& that ) -> strict_value { t ^= that.t; return *this; }
1589+
1590+
auto operator|( strict_value const& that ) const -> strict_value { return strict_value(t | that.t); }
1591+
auto operator&( strict_value const& that ) const -> strict_value { return strict_value(t & that.t); }
1592+
auto operator^( strict_value const& that ) const -> strict_value { return strict_value(t ^ that.t); }
1593+
1594+
auto is_default_value() const -> bool { return t == T{}; }
15651595
};
15661596

15671597

@@ -1627,19 +1657,6 @@ inline auto fopen( const char* filename, const char* mode ) {
16271657
// with cpp2::fopen as a starting example.
16281658

16291659

1630-
//-----------------------------------------------------------------------
1631-
//
1632-
// An implementation of GSL's narrow_cast with a clearly 'unsafe' name
1633-
//
1634-
//-----------------------------------------------------------------------
1635-
//
1636-
template <typename C, typename X>
1637-
auto unsafe_narrow( X&& x ) noexcept -> decltype(auto)
1638-
{
1639-
return static_cast<C>(CPP2_FORWARD(x));
1640-
}
1641-
1642-
16431660
//-----------------------------------------------------------------------
16441661
//
16451662
// Signed/unsigned comparison checks

regression-tests/pure2-enum.cpp2

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
skat_game: @enum type = {
3+
diamonds := 9;
4+
hearts; // 10
5+
spades; // 11
6+
clubs; // 12
7+
grand := 20;
8+
null := 23;
9+
}
10+
11+
rgb: @enum type = {
12+
red; // 0
13+
green; // 1
14+
blue; // 2
15+
}
16+
17+
file_attributes: @flag_enum type = {
18+
cached; // 1
19+
current; // 2
20+
obsolete; // 4
21+
}
22+
23+
main: () = {
24+
// x : skat_game = 9; // error, can't construct skat_game from integer
25+
26+
x := skat_game::clubs;
27+
28+
// if x == 9 { } // error, can't compare skat_game and integer
29+
// if x == rgb::red { } // error, can't compare skat_game and rgb color
30+
31+
std::cout << "with if else: ";
32+
if x == skat_game::diamonds { // ok, can compare two skat_games
33+
std::cout << "diamonds";
34+
}
35+
else if skat_game::hearts == x { // ok, in either order
36+
std::cout << "hearts";
37+
}
38+
else if x is (skat_game::spades) { // ok, using is
39+
std::cout << "spades";
40+
}
41+
else if skat_game::clubs is (x) { // ok, using is
42+
std::cout << "clubs";
43+
}
44+
else {
45+
std::cout << "not a suit";
46+
}
47+
48+
std::cout << "\nwith inspect: " << inspect x -> std::string {
49+
is (skat_game::diamonds) = "diamonds";
50+
is (skat_game::hearts ) = "hearts";
51+
is (skat_game::spades ) = "spades";
52+
is (skat_game::clubs ) = "clubs";
53+
is _ = "not a suit";
54+
} << std::endl;
55+
56+
// x = 9; // error, can't assign skat_game from integer
57+
// x = rgb::red; // error, can't assign skat_game from rgb color
58+
59+
x = skat_game::diamonds; // ok, can assign one skat_game from another
60+
61+
f := file_attributes::current | file_attributes::cached;
62+
f &= file_attributes::cached | file_attributes::obsolete;
63+
f |= file_attributes::current;
64+
65+
f2 := file_attributes::cached;
66+
67+
std::cout << "f as int is (f as int )$\n";
68+
std::cout << "f2 as int is (f2 as int )$\n";
69+
std::cout << "f == f2 is (f == f2 )$\n";
70+
71+
// The following are still in progress
72+
73+
// std::cout << "f is (f2) is (f is (f2))$\n";
74+
// std::cout << "f2 is (f ) is (f2 is (f ))$\n";
75+
76+
// if f is (file_attributes::cached ) { std::cout << "cached "; }
77+
// if f is (file_attributes::current ) { std::cout << "current "; }
78+
// if f is (file_attributes::obsolete) { std::cout << "obsolete "; }
79+
// if f is (file_attributes::none ) { std::cout << "none "; }
80+
// std::cout << "\n";
81+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
with if else: clubs
2+
with inspect: clubs
3+
f as int is 3
4+
f2 as int is 1
5+
f == f2 is 0

regression-tests/test-results/clang-12/pure2-enum.cpp.output

Whitespace-only changes.

regression-tests/test-results/gcc-10/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:36: error: expected ‘;’ at end of member declaration
22
In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7:
3-
../../../include/cpp2util.h:1770:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
4-
1770 | #define CPP2_REQUIRES_(...) static_assert(false, "GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.")
3+
../../../include/cpp2util.h:1787:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
4+
1787 | #define CPP2_REQUIRES_(...) static_assert(false, "GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.")
55
| ^~~~~
66
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’
77
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:46: error: expected ‘;’ at end of member declaration
88
In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7:
9-
../../../include/cpp2util.h:1770:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
10-
1770 | #define CPP2_REQUIRES_(...) static_assert(false, "GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.")
9+
../../../include/cpp2util.h:1787:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
10+
1787 | #define CPP2_REQUIRES_(...) static_assert(false, "GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.")
1111
| ^~~~~
1212
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’
1313
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:80&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type, std::__cxx11::string>’
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
with if else: clubs
2+
with inspect: clubs
3+
f as int is 3
4+
f2 as int is 1
5+
f == f2 is 0

regression-tests/test-results/gcc-10/pure2-enum.cpp.output

Whitespace-only changes.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
In file included from pure2-requires-clauses.cpp:7:
2-
../../../include/cpp2util.h:1770:33: error: expected unqualified-id before ‘static_assert’
3-
1770 | #define CPP2_REQUIRES_(...) static_assert(false, "GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.")
2+
../../../include/cpp2util.h:1787:33: error: expected unqualified-id before ‘static_assert’
3+
1787 | #define CPP2_REQUIRES_(...) static_assert(false, "GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.")
44
| ^~~~~~~~~~~~~
55
pure2-requires-clauses.cpp2:19:1: note: in expansion of macro ‘CPP2_REQUIRES_’
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
with if else: clubs
2+
with inspect: clubs
3+
f as int is 3
4+
f2 as int is 1
5+
f == f2 is 0

regression-tests/test-results/gcc-13/pure2-enum.cpp.output

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
with if else: clubs
2+
with inspect: clubs
3+
f as int is 3
4+
f2 as int is 1
5+
f == f2 is 0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pure2-enum.cpp

0 commit comments

Comments
 (0)