Skip to content

Commit 4d6c989

Browse files
committed
Add is and as support for std::expected
1 parent 0e74bd7 commit 4d6c989

10 files changed

+876
-2
lines changed

include/cpp2util.h

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,8 +1287,16 @@ auto as(X const& x) -> C
12871287

12881288
template< typename C, typename X >
12891289
auto as( X const& x ) -> auto
1290-
requires (!std::is_same_v<C, X> && !std::is_base_of_v<C, X> && requires { C{x}; }
1291-
&& !(std::is_same_v<C, std::string> && std::is_integral_v<X>) // exclude above case
1290+
requires (
1291+
(!std::is_same_v<C, X> && !std::is_base_of_v<C, X> && requires { C{x}; }
1292+
&& !(std::is_same_v<C, std::string> && std::is_integral_v<X>) // exclude above case
1293+
)
1294+
#ifdef __cpp_lib_expected
1295+
||
1296+
(!std::is_same_v<C, X> && !std::is_base_of_v<C, X> && requires { C{x}; }
1297+
&& !(std::is_same_v<C, std::unexpected<typename X::error_type>>) // exclude above case
1298+
)
1299+
#endif
12921300
)
12931301
{
12941302
// Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
@@ -1644,6 +1652,88 @@ constexpr auto as( X const& x ) -> decltype(auto)
16441652
{ return x.value(); }
16451653

16461654

1655+
//-------------------------------------------------------------------------------------------------------------
1656+
// std::expected is and as
1657+
//
1658+
#ifdef __cpp_lib_expected
1659+
// is Type
1660+
//
1661+
template<typename T, typename X>
1662+
requires std::is_same_v<X, std::expected<T, typename X::error_type>>
1663+
constexpr auto is( X const &x ) -> bool
1664+
{
1665+
return x.has_value();
1666+
}
1667+
1668+
template<typename T, typename U, typename V>
1669+
requires std::is_same_v<T, empty>
1670+
constexpr auto is( std::expected<U, V> const &x ) -> bool
1671+
{
1672+
return !x.has_value();
1673+
}
1674+
1675+
// is std::unexpected<T> Type
1676+
//
1677+
template<typename T, typename X>
1678+
requires (
1679+
std::is_same_v<T, std::unexpected<typename X::error_type>>
1680+
&& std::is_same_v<X, std::expected<typename X::value_type, typename X::error_type>>)
1681+
constexpr auto is( X const &x ) -> bool
1682+
{
1683+
return !x.has_value();
1684+
}
1685+
1686+
1687+
// is Value
1688+
//
1689+
template<typename T, typename U>
1690+
constexpr auto is( std::expected<T, U> const &x, auto &&value ) -> bool
1691+
{
1692+
// Predicate case
1693+
if constexpr (requires{ bool{ value(x) }; }) {
1694+
return value(x);
1695+
}
1696+
else if constexpr (std::is_function_v<decltype(value)> || requires{ &value.operator(); }) {
1697+
return false;
1698+
}
1699+
1700+
// Value case
1701+
else if constexpr (requires{ bool{ x.value() == value }; }) {
1702+
return x.has_value() && x.value() == value;
1703+
}
1704+
return false;
1705+
}
1706+
1707+
1708+
// as
1709+
//
1710+
template<typename T, typename X>
1711+
requires std::is_same_v<X, std::expected<T, typename X::error_type>>
1712+
constexpr auto as( X const &x ) -> decltype(auto)
1713+
{
1714+
return x.value();
1715+
}
1716+
1717+
// as std::unexpected<T>
1718+
//
1719+
template<typename T, typename X>
1720+
requires (
1721+
std::is_same_v<T, std::unexpected<typename X::error_type>>
1722+
&& std::is_same_v<X, std::expected<typename X::value_type, typename X::error_type>>)
1723+
constexpr auto as( X const &x ) -> decltype(auto)
1724+
{
1725+
// It's UB to call `error` if `has_value` is true.
1726+
if (x.has_value()) {
1727+
Throw(
1728+
std::runtime_error("Cannot cast 'expected' to 'unexpected' because it has a value"),
1729+
"Cannot cast 'expected' to 'unexpected' because it has a value");
1730+
}
1731+
1732+
return std::unexpected< typename X::error_type>(x.error());
1733+
}
1734+
#endif
1735+
1736+
16471737
//-----------------------------------------------------------------------
16481738
//
16491739
// A variation of GSL's final_action_success / finally
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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
pure2-expected-is-as.cpp2:7:10: error: no member named 'expected' in namespace 'std'
2+
std::expected<int,int> ex1 {123};
3+
~~~~~^
4+
pure2-expected-is-as.cpp2:7:22: error: expected '(' for function-style cast or type construction
5+
std::expected<int,int> ex1 {123};
6+
~~~^
7+
pure2-expected-is-as.cpp2:8:10: error: no member named 'expected' in namespace 'std'
8+
std::expected<int,int> ex2 {std::unexpected(-1)};
9+
~~~~~^
10+
pure2-expected-is-as.cpp2:8:22: error: expected '(' for function-style cast or type construction
11+
std::expected<int,int> ex2 {std::unexpected(-1)};
12+
~~~^
13+
pure2-expected-is-as.cpp2:9:10: error: no member named 'expected' in namespace 'std'
14+
std::expected<std::string,size_t> ex3 {"Expect the unexpected"};
15+
~~~~~^
16+
pure2-expected-is-as.cpp2:9:30: error: expected '(' for function-style cast or type construction
17+
std::expected<std::string,size_t> ex3 {"Expect the unexpected"};
18+
~~~~~~~~~~~^
19+
In file included from pure2-expected-is-as.cpp:7:
20+
In file included from ../../../include/cpp2util.h:46:
21+
In file included from /Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/algorithm:1712:
22+
In file included from /Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/memory:877:
23+
In file included from /Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iterator:684:
24+
In file included from /Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__iterator/common_iterator.h:22:
25+
/Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/variant:1282:3: error: static_assert failed due to requirement '0 < sizeof...(_Types)' "variant must consist of at least one alternative."
26+
static_assert(0 < sizeof...(_Types),
27+
^ ~~~~~~~~~~~~~~~~~~~~~
28+
pure2-expected-is-as.cpp2:11:23: note: in instantiation of template class 'std::variant<>' requested here
29+
if (cpp2::is<int>(ex1)) {
30+
^
31+
pure2-expected-is-as.cpp2:11:23: error: use of undeclared identifier 'ex1'
32+
if (cpp2::is<int>(ex1)) {
33+
^
34+
pure2-expected-is-as.cpp2:15:24: error: use of undeclared identifier 'ex1'
35+
if (cpp2::is<bool>(ex1)) {
36+
^
37+
pure2-expected-is-as.cpp2:20:24: error: use of undeclared identifier 'ex1'
38+
if (cpp2::is<void>(ex1)) {
39+
^
40+
pure2-expected-is-as.cpp2:25:23: error: no member named 'unexpected' in namespace 'std'
41+
if (cpp2::is<std::unexpected<int>>(ex1)) {
42+
~~~~~^
43+
pure2-expected-is-as.cpp2:25:37: error: expected '(' for function-style cast or type construction
44+
if (cpp2::is<std::unexpected<int>>(ex1)) {
45+
~~~^
46+
pure2-expected-is-as.cpp2:25:40: error: use of undeclared identifier 'ex1'; did you mean 'exp'?
47+
if (cpp2::is<std::unexpected<int>>(ex1)) {
48+
^~~
49+
exp
50+
/Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/math.h:895:1: note: 'exp' declared here
51+
exp(_A1 __lcpp_x) _NOEXCEPT {return ::exp((double)__lcpp_x);}
52+
^
53+
pure2-expected-is-as.cpp2:30:18: error: use of undeclared identifier 'ex1'
54+
if (cpp2::is(ex1, 123)) {
55+
^
56+
pure2-expected-is-as.cpp2:34:18: error: use of undeclared identifier 'ex1'
57+
if (cpp2::is(ex1, 100)) {
58+
^
59+
pure2-expected-is-as.cpp2:39:31: error: use of undeclared identifier 'ex1'
60+
auto val1 {cpp2::as_<int>(ex1)};
61+
^
62+
pure2-expected-is-as.cpp2:42:23: error: use of undeclared identifier 'ex2'
63+
if (cpp2::is<int>(ex2)) {
64+
^
65+
pure2-expected-is-as.cpp2:47:24: error: use of undeclared identifier 'ex2'
66+
if (cpp2::is<bool>(ex2)) {
67+
^
68+
pure2-expected-is-as.cpp2:52:18: error: use of undeclared identifier 'ex2'
69+
if (cpp2::is(ex2, 123)) {
70+
^
71+
fatal error: too many errors emitted, stopping now [-ferror-limit=]
72+
20 errors generated.

0 commit comments

Comments
 (0)