Skip to content

Commit afc1b10

Browse files
committed
Add is and as support for std::expected
1 parent 1032a5b commit afc1b10

File tree

7 files changed

+379
-0
lines changed

7 files changed

+379
-0
lines changed

include/cpp2util.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,6 +2078,93 @@ constexpr auto as( X&& x ) -> decltype(auto) {
20782078
}
20792079

20802080

2081+
2082+
//-------------------------------------------------------------------------------------------------------------
2083+
// std::expected is and as
2084+
//
2085+
#ifdef __cpp_lib_expected
2086+
2087+
// is Type
2088+
//
2089+
template<typename T, typename X>
2090+
requires std::is_same_v<X, std::expected<T, typename X::error_type>>
2091+
constexpr auto is(X const& x) -> bool
2092+
{
2093+
return x.has_value();
2094+
}
2095+
2096+
template<typename T, typename U, typename V>
2097+
requires std::is_same_v<T, empty>
2098+
constexpr auto is(std::expected<U, V> const& x) -> bool
2099+
{
2100+
return !x.has_value();
2101+
}
2102+
2103+
// is std::unexpected<T> Type
2104+
//
2105+
template<typename T, typename X>
2106+
requires (
2107+
std::is_same_v<T, std::unexpected<typename X::error_type>>
2108+
&& std::is_same_v<X, std::expected<typename X::value_type, typename X::error_type>>
2109+
)
2110+
constexpr auto is(X const& x) -> bool
2111+
{
2112+
return !x.has_value();
2113+
}
2114+
2115+
2116+
// is Value
2117+
//
2118+
template<typename T, typename U>
2119+
constexpr auto is(std::expected<T, U> const& x, auto&& value) -> bool
2120+
{
2121+
// Predicate case
2122+
if constexpr (requires{ bool{ value(x) }; }) {
2123+
return value(x);
2124+
}
2125+
else if constexpr (std::is_function_v<decltype(value)> || requires{ &value.operator(); }) {
2126+
return false;
2127+
}
2128+
2129+
// Value case
2130+
else if constexpr (requires{ bool{ x.value() == value }; }) {
2131+
return x.has_value() && x.value() == value;
2132+
}
2133+
else {
2134+
return false;
2135+
}
2136+
}
2137+
2138+
2139+
// as
2140+
//
2141+
template<typename T, typename X>
2142+
requires std::is_same_v<X, std::expected<T, typename X::error_type>>
2143+
constexpr auto as(X const& x) -> decltype(auto)
2144+
{
2145+
return x.value();
2146+
}
2147+
2148+
// as std::unexpected<T>
2149+
//
2150+
template<typename T, typename X>
2151+
requires (
2152+
std::is_same_v<T, std::unexpected<typename X::error_type>>
2153+
&& std::is_same_v<X, std::expected<typename X::value_type, typename X::error_type>>
2154+
)
2155+
constexpr auto as(X const& x) -> decltype(auto)
2156+
{
2157+
// It's UB to call `error` if `has_value` is true.
2158+
if (x.has_value()) {
2159+
Throw(
2160+
std::runtime_error("Cannot cast 'expected' to 'unexpected' because it has a value"),
2161+
"Cannot cast 'expected' to 'unexpected' because it has a value");
2162+
}
2163+
2164+
return std::unexpected<typename X::error_type>(x.error());
2165+
}
2166+
#endif
2167+
20812168
} // impl
20822169

20832170

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// `std::expected` requires C++23 so a dedicated test file is needed
2+
// since only MSVC supports it at time of writing, and there's no #ifdef
3+
// or `static if` support in Cpp2 (yet?).
4+
5+
main: () -> int = {
6+
7+
ex1: std::expected<int, int> = (123);
8+
ex2: std::expected<int, int> = std::unexpected(-1);
9+
ex3: std::expected<std::string, size_t> = ("Expect the unexpected");
10+
11+
if ex1 is int {
12+
std::cout << "ex1 is int\n";
13+
}
14+
15+
if ex1 is bool {
16+
std::cout << "BUG - ex1 is not a bool\n";
17+
return -1;
18+
}
19+
20+
if ex1 is void {
21+
std::cout << "BUG - ex1 is not 'empty'\n";
22+
return -1;
23+
}
24+
25+
if ex1 is std::unexpected<int> {
26+
std::cout << "BUG - ex1 is not unexpected\n";
27+
return -1;
28+
}
29+
30+
if ex1 is 123 {
31+
std::cout << "ex1 is 123\n";
32+
}
33+
34+
if ex1 is 100 {
35+
std::cout << "BUG - ex1's value is not 100\n";
36+
return -1;
37+
}
38+
39+
val1:= ex1 as int;
40+
std::cout << "ex1 as int = " << val1 << "\n";
41+
42+
if ex2 is int {
43+
std::cout << "BUG - ex2 is not an int\n";
44+
return -1;
45+
}
46+
47+
if ex2 is bool {
48+
std::cout << "BUG - ex2 is not a bool\n";
49+
return -1;
50+
}
51+
52+
if ex2 is 123 {
53+
std::cout << "BUG - ex2 does not have a value\n";
54+
return -1;
55+
}
56+
57+
if ex2 is std::unexpected<int> {
58+
std::cout << "ex2 is unexpected<int> and error is: " << ex2.error() << "\n";
59+
}
60+
61+
if ex2 is void {
62+
std::cout << "ex2 is 'empty' aka unexpected<int> and error is: " << ex2.error() << "\n";
63+
}
64+
65+
ex2_err:= ex2 as std::unexpected<int>;
66+
std::cout << "ex2 as std::unexpected<int> and error = " << ex2_err.error() << "\n";
67+
68+
test_inspect(ex1, "expected<int, int> with value");
69+
test_inspect(ex2, "expected<int, int> with unexpected");
70+
test_inspect(ex3, "expected<string, size_t> with value");
71+
72+
return 0;
73+
}
74+
75+
test_inspect: ( x: _, msg: _ ) = {
76+
77+
unwrap:= :(unexp: std::unexpected<int>) -> _ = {
78+
return unexp.error();
79+
};
80+
81+
std::cout
82+
<< "\n" << msg << "\n ..."
83+
<< inspect x -> std::string {
84+
is int = "integer " + std::to_string(x as int);
85+
is std::unexpected<int> = "unexpected<int> " + std::to_string(unwrap(x as std::unexpected<int>));
86+
is std::string = "string " + x as std::string;
87+
is _ = " no match";
88+
}
89+
<< "\n";
90+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
pure2-expected-is-as.cpp
2+
pure2-expected-is-as.cpp2(7): error C2039: 'expected': is not a member of 'std'
3+
predefined C++ types (compiler internal)(346): note: see declaration of 'std'
4+
pure2-expected-is-as.cpp2(7): error C2062: type 'int' unexpected
5+
pure2-expected-is-as.cpp2(7): error C2143: syntax error: missing ';' before '{'
6+
pure2-expected-is-as.cpp2(7): error C2143: syntax error: missing ';' before '}'
7+
pure2-expected-is-as.cpp2(8): error C2039: 'expected': is not a member of 'std'
8+
predefined C++ types (compiler internal)(346): note: see declaration of 'std'
9+
pure2-expected-is-as.cpp2(8): error C2062: type 'int' unexpected
10+
pure2-expected-is-as.cpp2(8): error C2143: syntax error: missing ';' before '{'
11+
pure2-expected-is-as.cpp2(8): error C2039: 'unexpected': is not a member of 'std'
12+
predefined C++ types (compiler internal)(346): note: see declaration of 'std'
13+
pure2-expected-is-as.cpp2(8): error C2660: 'unexpected': function does not take 1 arguments
14+
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.41.34120\include\eh.h(33): note: see declaration of 'unexpected'
15+
pure2-expected-is-as.cpp2(8): note: while trying to match the argument list '(int)'
16+
pure2-expected-is-as.cpp2(8): error C2143: syntax error: missing ';' before '}'
17+
pure2-expected-is-as.cpp2(9): error C2039: 'expected': is not a member of 'std'
18+
predefined C++ types (compiler internal)(346): note: see declaration of 'std'
19+
pure2-expected-is-as.cpp2(9): error C2275: 'std::string': expected an expression instead of a type
20+
pure2-expected-is-as.cpp2(9): error C2065: 'ex3': undeclared identifier
21+
pure2-expected-is-as.cpp2(9): error C2275: 'size_t': expected an expression instead of a type
22+
pure2-expected-is-as.cpp2(11): error C2065: 'ex1': undeclared identifier
23+
pure2-expected-is-as.cpp2(15): error C2065: 'ex1': undeclared identifier
24+
pure2-expected-is-as.cpp2(20): error C2065: 'ex1': undeclared identifier
25+
pure2-expected-is-as.cpp2(25): error C2039: 'unexpected': is not a member of 'std'
26+
predefined C++ types (compiler internal)(346): note: see declaration of 'std'
27+
pure2-expected-is-as.cpp2(25): error C2062: type 'int' unexpected
28+
pure2-expected-is-as.cpp2(25): error C2059: syntax error: '>'
29+
pure2-expected-is-as.cpp2(25): error C2143: syntax error: missing ';' before '{'
30+
pure2-expected-is-as.cpp2(30): error C2065: 'ex1': undeclared identifier
31+
pure2-expected-is-as.cpp2(34): error C2065: 'ex1': undeclared identifier
32+
pure2-expected-is-as.cpp2(39): error C2065: 'ex1': undeclared identifier
33+
pure2-expected-is-as.cpp2(39): error C2119: 'val1': the type for 'auto' cannot be deduced from an empty initializer
34+
pure2-expected-is-as.cpp2(42): error C2065: 'ex2': undeclared identifier
35+
pure2-expected-is-as.cpp2(47): error C2065: 'ex2': undeclared identifier
36+
pure2-expected-is-as.cpp2(52): error C2065: 'ex2': undeclared identifier
37+
pure2-expected-is-as.cpp2(57): error C2039: 'unexpected': is not a member of 'std'
38+
predefined C++ types (compiler internal)(346): note: see declaration of 'std'
39+
pure2-expected-is-as.cpp2(57): error C2062: type 'int' unexpected
40+
pure2-expected-is-as.cpp2(57): error C2059: syntax error: '>'
41+
pure2-expected-is-as.cpp2(57): error C2143: syntax error: missing ';' before '{'
42+
pure2-expected-is-as.cpp2(58): error C2065: 'ex2': undeclared identifier
43+
pure2-expected-is-as.cpp2(61): error C2065: 'ex2': undeclared identifier
44+
pure2-expected-is-as.cpp2(62): error C2065: 'ex2': undeclared identifier
45+
pure2-expected-is-as.cpp2(65): error C2039: 'unexpected': is not a member of 'std'
46+
predefined C++ types (compiler internal)(346): note: see declaration of 'std'
47+
pure2-expected-is-as.cpp2(65): error C2062: type 'int' unexpected
48+
pure2-expected-is-as.cpp2(65): error C2062: type 'unknown-type' unexpected
49+
pure2-expected-is-as.cpp2(65): error C2143: syntax error: missing ';' before '}'
50+
pure2-expected-is-as.cpp2(66): error C2143: syntax error: missing ';' before '<<'
51+
pure2-expected-is-as.cpp2(66): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
52+
pure2-expected-is-as.cpp2(66): error C2371: 'std::cout': redefinition; different basic types
53+
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.41.34120\include\iostream(39): note: see declaration of 'std::cout'
54+
pure2-expected-is-as.cpp2(66): error C2059: syntax error: '<<'
55+
pure2-expected-is-as.cpp2(66): error C2143: syntax error: missing ';' before '{'
56+
pure2-expected-is-as.cpp2(66): error C2447: '{': missing function header (old-style formal list?)
57+
pure2-expected-is-as.cpp2(66): error C2059: syntax error: '||'
58+
pure2-expected-is-as.cpp2(66): error C2065: 'Obj': undeclared identifier
59+
pure2-expected-is-as.cpp2(66): error C2065: 'Params': undeclared identifier
60+
pure2-expected-is-as.cpp2(66): error C2059: syntax error: '}'
61+
pure2-expected-is-as.cpp2(66): error C2143: syntax error: missing ')' before ';'
62+
pure2-expected-is-as.cpp2(66): error C2059: syntax error: 'requires'
63+
pure2-expected-is-as.cpp2(66): error C2059: syntax error: ')'
64+
pure2-expected-is-as.cpp2(66): error C2065: 'obj': undeclared identifier
65+
pure2-expected-is-as.cpp2(66): error C3553: decltype expects an expression not a type
66+
pure2-expected-is-as.cpp2(66): error C2065: 'params': undeclared identifier
67+
pure2-expected-is-as.cpp2(66): fatal error C1003: error count exceeds 100; stopping compilation
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
ex1 is int
2+
ex1 is 123
3+
ex1 as int = 123
4+
ex2 is unexpected<int> and error is: -1
5+
ex2 is 'empty' aka unexpected<int> and error is: -1
6+
ex2 as std::unexpected<int> and error = -1
7+
8+
expected<int, int> with value
9+
...integer 123
10+
11+
expected<int, int> with unexpected
12+
...unexpected<int> -1
13+
14+
expected<string, size_t> with value
15+
...string Expect the unexpected
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pure2-expected-is-as.cpp
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
#line 1 "pure2-expected-is-as.cpp2"
10+
11+
12+
//=== Cpp2 type definitions and function declarations ===========================
13+
14+
#line 1 "pure2-expected-is-as.cpp2"
15+
// `std::expected` requires C++23 so a dedicated test file is needed
16+
// since only MSVC supports it at time of writing, and there's no #ifdef
17+
// or `static if` support in Cpp2 (yet?).
18+
19+
#line 5 "pure2-expected-is-as.cpp2"
20+
[[nodiscard]] auto main() -> int;
21+
22+
#line 75 "pure2-expected-is-as.cpp2"
23+
auto test_inspect(auto const& x, auto const& msg) -> void;
24+
25+
//=== Cpp2 function definitions =================================================
26+
27+
#line 1 "pure2-expected-is-as.cpp2"
28+
29+
#line 5 "pure2-expected-is-as.cpp2"
30+
[[nodiscard]] auto main() -> int{
31+
32+
std::expected<int,int> ex1 {123};
33+
std::expected<int,int> ex2 {std::unexpected(-1)};
34+
std::expected<std::string,size_t> ex3 {"Expect the unexpected"};
35+
36+
if (cpp2::impl::is<int>(ex1)) {
37+
std::cout << "ex1 is int\n";
38+
}
39+
40+
if (cpp2::impl::is<bool>(ex1)) {
41+
std::cout << "BUG - ex1 is not a bool\n";
42+
return -1;
43+
}
44+
45+
if (cpp2::impl::is<void>(ex1)) {
46+
std::cout << "BUG - ex1 is not 'empty'\n";
47+
return -1;
48+
}
49+
50+
if (cpp2::impl::is<std::unexpected<int>>(ex1)) {
51+
std::cout << "BUG - ex1 is not unexpected\n";
52+
return -1;
53+
}
54+
55+
if (cpp2::impl::is(ex1, 123)) {
56+
std::cout << "ex1 is 123\n";
57+
}
58+
59+
if (cpp2::impl::is(ex1, 100)) {
60+
std::cout << "BUG - ex1's value is not 100\n";
61+
return -1;
62+
}
63+
64+
auto val1 {cpp2::impl::as_<int>(ex1)};
65+
std::cout << "ex1 as int = " << cpp2::move(val1) << "\n";
66+
67+
if (cpp2::impl::is<int>(ex2)) {
68+
std::cout << "BUG - ex2 is not an int\n";
69+
return -1;
70+
}
71+
72+
if (cpp2::impl::is<bool>(ex2)) {
73+
std::cout << "BUG - ex2 is not a bool\n";
74+
return -1;
75+
}
76+
77+
if (cpp2::impl::is(ex2, 123)) {
78+
std::cout << "BUG - ex2 does not have a value\n";
79+
return -1;
80+
}
81+
82+
if (cpp2::impl::is<std::unexpected<int>>(ex2)) {
83+
std::cout << "ex2 is unexpected<int> and error is: " << CPP2_UFCS(error)(ex2) << "\n";
84+
}
85+
86+
if (cpp2::impl::is<void>(ex2)) {
87+
std::cout << "ex2 is 'empty' aka unexpected<int> and error is: " << CPP2_UFCS(error)(ex2) << "\n";
88+
}
89+
90+
auto ex2_err {cpp2::impl::as_<std::unexpected<int>>(ex2)};
91+
std::cout << "ex2 as std::unexpected<int> and error = " << CPP2_UFCS(error)(cpp2::move(ex2_err)) << "\n";
92+
93+
test_inspect(cpp2::move(ex1), "expected<int, int> with value");
94+
test_inspect(cpp2::move(ex2), "expected<int, int> with unexpected");
95+
test_inspect(cpp2::move(ex3), "expected<string, size_t> with value");
96+
97+
return 0;
98+
}
99+
100+
#line 75 "pure2-expected-is-as.cpp2"
101+
auto test_inspect(auto const& x, auto const& msg) -> void{
102+
103+
auto unwrap {[](cpp2::impl::in<std::unexpected<int>> unexp) -> auto{
104+
return CPP2_UFCS(error)(unexp);
105+
}};
106+
107+
std::cout
108+
<< "\n" << msg << "\n ..."
109+
<< [&] () -> std::string { auto&& _expr = x;
110+
if (cpp2::impl::is<int>(_expr)) { if constexpr( requires{"integer " + std::to_string(cpp2::impl::as<int>(x));} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("integer " + std::to_string(cpp2::impl::as<int>(x)))),std::string> ) return "integer " + std::to_string(cpp2::impl::as<int>(x)); else return std::string{}; else return std::string{}; }
111+
else if (cpp2::impl::is<std::unexpected<int>>(_expr)) { if constexpr( requires{"unexpected<int> " + std::to_string(cpp2::move(unwrap)(cpp2::impl::as<std::unexpected<int>>(x)));} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("unexpected<int> " + std::to_string(cpp2::move(unwrap)(cpp2::impl::as<std::unexpected<int>>(x))))),std::string> ) return "unexpected<int> " + std::to_string(cpp2::move(unwrap)(cpp2::impl::as<std::unexpected<int>>(x))); else return std::string{}; else return std::string{}; }
112+
else if (cpp2::impl::is<std::string>(_expr)) { if constexpr( requires{"string " + cpp2::impl::as<std::string>(x);} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("string " + cpp2::impl::as<std::string>(x))),std::string> ) return "string " + cpp2::impl::as<std::string>(x); else return std::string{}; else return std::string{}; }
113+
else return " no match"; }
114+
()
115+
<< "\n";
116+
}
117+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-expected-is-as.cpp2... ok (all Cpp2, passes safety checks)
2+

0 commit comments

Comments
 (0)