Skip to content

Commit 2f37277

Browse files
committed
Add inspect ... is value support for variant, any, and optional
And a facelift for `nonesuch` Also eliminate the use of C-style variadic `(...)` for two catchall fallback functions, and use `(auto const&)` instead And hey, why not expand `variant` support from 10 to 20 types while we're at it
1 parent c77de70 commit 2f37277

10 files changed

+157
-42
lines changed

include/cpp2util.h

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,10 @@ class out {
568568

569569
// For use when returning "no such thing", such as
570570
// when customizing is/as for std::variant
571-
static std::nullptr_t nonesuch = nullptr;
571+
struct nonesuch_ {
572+
auto operator==(auto const&) -> bool { return false; }
573+
};
574+
static nonesuch_ nonesuch;
572575

573576
// For designating "holds no value" -- used only with is, not as
574577
// TODO: Does this really warrant a new synonym? Perhaps "is void" is enough
@@ -619,25 +622,18 @@ auto is( X const& x ) -> bool {
619622
//-------------------------------------------------------------------------------------------------------------
620623
// Built-in is (values)
621624
//
622-
623625
inline constexpr auto is( auto const& x, auto const& value ) -> bool
624626
requires requires{ x == value; }
625627
{
626628
return x == value;
627629
}
628630

629-
inline constexpr auto is( auto const& x, auto const& value ) -> bool
630-
requires (!requires{ x == value; })
631-
{
632-
return false;
633-
}
634-
635631

636632
//-------------------------------------------------------------------------------------------------------------
637633
// Built-in as (partial)
638634
//
639635
template< typename C >
640-
auto as(...) -> auto {
636+
auto as(auto const&) -> auto {
641637
return nonesuch;
642638
}
643639

@@ -693,6 +689,32 @@ constexpr auto operator_is( std::variant<Ts...> const& x ) {
693689
return x.index();
694690
}
695691

692+
template<typename... Ts>
693+
constexpr auto is( std::variant<Ts...> const& x, auto const& value ) -> bool
694+
{
695+
if constexpr (requires{ operator_as< 0>(x) == value; }) if (x.index() == 0) return operator_as< 0>(x) == value;;
696+
if constexpr (requires{ operator_as< 1>(x) == value; }) if (x.index() == 1) return operator_as< 1>(x) == value;;
697+
if constexpr (requires{ operator_as< 2>(x) == value; }) if (x.index() == 2) return operator_as< 2>(x) == value;;
698+
if constexpr (requires{ operator_as< 3>(x) == value; }) if (x.index() == 3) return operator_as< 3>(x) == value;;
699+
if constexpr (requires{ operator_as< 4>(x) == value; }) if (x.index() == 4) return operator_as< 4>(x) == value;;
700+
if constexpr (requires{ operator_as< 5>(x) == value; }) if (x.index() == 5) return operator_as< 5>(x) == value;;
701+
if constexpr (requires{ operator_as< 6>(x) == value; }) if (x.index() == 6) return operator_as< 6>(x) == value;;
702+
if constexpr (requires{ operator_as< 7>(x) == value; }) if (x.index() == 7) return operator_as< 7>(x) == value;;
703+
if constexpr (requires{ operator_as< 8>(x) == value; }) if (x.index() == 8) return operator_as< 8>(x) == value;;
704+
if constexpr (requires{ operator_as< 9>(x) == value; }) if (x.index() == 9) return operator_as< 9>(x) == value;;
705+
if constexpr (requires{ operator_as<10>(x) == value; }) if (x.index() == 10) return operator_as<10>(x) == value;;
706+
if constexpr (requires{ operator_as<11>(x) == value; }) if (x.index() == 11) return operator_as<11>(x) == value;;
707+
if constexpr (requires{ operator_as<12>(x) == value; }) if (x.index() == 12) return operator_as<12>(x) == value;;
708+
if constexpr (requires{ operator_as<13>(x) == value; }) if (x.index() == 13) return operator_as<13>(x) == value;;
709+
if constexpr (requires{ operator_as<14>(x) == value; }) if (x.index() == 14) return operator_as<14>(x) == value;;
710+
if constexpr (requires{ operator_as<15>(x) == value; }) if (x.index() == 15) return operator_as<15>(x) == value;;
711+
if constexpr (requires{ operator_as<16>(x) == value; }) if (x.index() == 16) return operator_as<16>(x) == value;;
712+
if constexpr (requires{ operator_as<17>(x) == value; }) if (x.index() == 17) return operator_as<17>(x) == value;;
713+
if constexpr (requires{ operator_as<18>(x) == value; }) if (x.index() == 18) return operator_as<18>(x) == value;;
714+
if constexpr (requires{ operator_as<19>(x) == value; }) if (x.index() == 19) return operator_as<19>(x) == value;;
715+
return false;
716+
}
717+
696718
template<size_t I, typename... Ts>
697719
constexpr auto operator_as( std::variant<Ts...> const& x ) -> auto&& {
698720
if constexpr (I < std::variant_size_v<std::variant<Ts...>>) {
@@ -709,16 +731,26 @@ inline constexpr auto is_any = std::disjunction_v<std::is_same<T, Ts>...>;
709731

710732
template<typename T, typename... Ts>
711733
auto is( std::variant<Ts...> const& x ) {
712-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<0>(x)), T >) if (x.index() == 0) return true;
713-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<1>(x)), T >) if (x.index() == 1) return true;
714-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<2>(x)), T >) if (x.index() == 2) return true;
715-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<3>(x)), T >) if (x.index() == 3) return true;
716-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<4>(x)), T >) if (x.index() == 4) return true;
717-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<5>(x)), T >) if (x.index() == 5) return true;
718-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<6>(x)), T >) if (x.index() == 6) return true;
719-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<7>(x)), T >) if (x.index() == 7) return true;
720-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<8>(x)), T >) if (x.index() == 8) return true;
721-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<9>(x)), T >) if (x.index() == 9) return true;
734+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) if (x.index() == 0) return true;
735+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 1>(x)), T >) if (x.index() == 1) return true;
736+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 2>(x)), T >) if (x.index() == 2) return true;
737+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) if (x.index() == 3) return true;
738+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 4>(x)), T >) if (x.index() == 4) return true;
739+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 5>(x)), T >) if (x.index() == 5) return true;
740+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 6>(x)), T >) if (x.index() == 6) return true;
741+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 7>(x)), T >) if (x.index() == 7) return true;
742+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) if (x.index() == 8) return true;
743+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 9>(x)), T >) if (x.index() == 9) return true;
744+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<10>(x)), T >) if (x.index() == 10) return true;
745+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) if (x.index() == 11) return true;
746+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) if (x.index() == 12) return true;
747+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) if (x.index() == 13) return true;
748+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<14>(x)), T >) if (x.index() == 14) return true;
749+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) if (x.index() == 15) return true;
750+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<16>(x)), T >) if (x.index() == 16) return true;
751+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) if (x.index() == 17) return true;
752+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) if (x.index() == 18) return true;
753+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<19>(x)), T >) if (x.index() == 19) return true;
722754
if constexpr (std::is_same_v< T, empty > ) {
723755
if (x.valueless_by_exception()) return true;
724756
// Need to guard this with is_any otherwise the get_if is illegal
@@ -729,16 +761,26 @@ auto is( std::variant<Ts...> const& x ) {
729761

730762
template<typename T, typename... Ts>
731763
auto as( std::variant<Ts...> const& x ) {
732-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<0>(x)), T >) if (x.index() == 0) return operator_as<0>(x);
733-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<1>(x)), T >) if (x.index() == 1) return operator_as<1>(x);
734-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<2>(x)), T >) if (x.index() == 2) return operator_as<2>(x);
735-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<3>(x)), T >) if (x.index() == 3) return operator_as<3>(x);
736-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<4>(x)), T >) if (x.index() == 4) return operator_as<4>(x);
737-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<5>(x)), T >) if (x.index() == 5) return operator_as<5>(x);
738-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<6>(x)), T >) if (x.index() == 6) return operator_as<6>(x);
739-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<7>(x)), T >) if (x.index() == 7) return operator_as<7>(x);
740-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<8>(x)), T >) if (x.index() == 8) return operator_as<8>(x);
741-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<9>(x)), T >) if (x.index() == 9) return operator_as<9>(x);
764+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) if (x.index() == 0) return operator_as<0>(x);
765+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 1>(x)), T >) if (x.index() == 1) return operator_as<1>(x);
766+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 2>(x)), T >) if (x.index() == 2) return operator_as<2>(x);
767+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) if (x.index() == 3) return operator_as<3>(x);
768+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 4>(x)), T >) if (x.index() == 4) return operator_as<4>(x);
769+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 5>(x)), T >) if (x.index() == 5) return operator_as<5>(x);
770+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 6>(x)), T >) if (x.index() == 6) return operator_as<6>(x);
771+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 7>(x)), T >) if (x.index() == 7) return operator_as<7>(x);
772+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) if (x.index() == 8) return operator_as<8>(x);
773+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 9>(x)), T >) if (x.index() == 9) return operator_as<9>(x);
774+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<10>(x)), T >) if (x.index() == 10) return operator_as<0>(x);
775+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) if (x.index() == 11) return operator_as<1>(x);
776+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) if (x.index() == 12) return operator_as<2>(x);
777+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) if (x.index() == 13) return operator_as<3>(x);
778+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<14>(x)), T >) if (x.index() == 14) return operator_as<4>(x);
779+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) if (x.index() == 15) return operator_as<5>(x);
780+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<16>(x)), T >) if (x.index() == 16) return operator_as<6>(x);
781+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) if (x.index() == 17) return operator_as<7>(x);
782+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) if (x.index() == 18) return operator_as<8>(x);
783+
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<19>(x)), T >) if (x.index() == 19) return operator_as<9>(x);
742784
throw std::bad_variant_access();
743785
}
744786

@@ -756,6 +798,12 @@ template<typename T, typename X>
756798
constexpr auto is( X const& x ) -> bool
757799
{ return !x.has_value(); }
758800

801+
constexpr auto is( std::any const& x, auto const& value ) -> bool
802+
{
803+
auto pvalue = std::any_cast<CPP2_TYPEOF(value)>(&x);
804+
return pvalue && *pvalue == value;
805+
}
806+
759807
template<typename T, typename X>
760808
requires (!std::is_reference_v<T> && std::is_same_v<X,std::any> && !std::is_same_v<T,std::any>)
761809
constexpr auto as( X const& x ) -> T
@@ -775,6 +823,20 @@ template<typename T, typename U>
775823
constexpr auto is( std::optional<U> const& x ) -> bool
776824
{ return !x.has_value(); }
777825

826+
template<typename T>
827+
constexpr auto is( std::optional<T> const& x, auto const& value ) -> bool
828+
requires requires{ x.value() == value; }
829+
{
830+
return x.has_value() && x.value() == value;
831+
}
832+
833+
template<typename T>
834+
constexpr auto is( std::optional<T> const& x, auto const& value ) -> bool
835+
requires (!requires{ x.value() == value; })
836+
{
837+
return false;
838+
}
839+
778840
template<typename T, typename X>
779841
requires std::is_same_v<X,std::optional<T>>
780842
constexpr auto as( X const& x ) -> auto&&
@@ -839,13 +901,22 @@ inline auto to_string(...) -> std::string {
839901
return "(customize me - no cpp2::to_string overload exists for this type)";
840902
}
841903

904+
inline auto to_string(std::any const&) -> std::string {
905+
return "std::any";
906+
}
907+
842908
template<typename T>
843909
inline auto to_string(T const& t) -> std::string
844910
requires requires { std::to_string(t); }
845911
{
846912
return std::to_string(t);
847913
}
848914

915+
inline auto to_string(char const* s) -> std::string
916+
{
917+
return std::string{s};
918+
}
919+
849920
inline auto to_string(std::string const& s) -> std::string const&
850921
{
851922
return s;
Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
main: ()->int = {
2-
test(42);
3-
test(3.14);
2+
v: std::variant<double, std::string, double> = ();
3+
test(v);
4+
v = "rev dodgson";
5+
test(v);
6+
7+
o: std::optional<int> = ();
8+
test(o);
9+
o = 42;
10+
test(o);
11+
12+
a: std::any = 0;
13+
test(a);
14+
a = "plugh" as std::string;
15+
test(a);
16+
417
test(0);
518
test(-42);
619
test("xyzzy" as std::string);
20+
test(3.14);
721
}
822

923
test: (x:_) = {
@@ -12,7 +26,7 @@ test: (x:_) = {
1226
is 0 = "zero";
1327
is (forty_two) = "the answer";
1428
is int = "integer " + cpp2::to_string(x);
15-
is std::string = x;
29+
is std::string = x as std::string;
1630
is _ = "(no match)";
1731
} << "\n";
1832
}

regression-tests/test-results/clang-12/mixed-string-interpolation.cpp.execution

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ p = (first, (empty))
1414
t = (3.140000, (empty), (empty))
1515
vv = 0
1616
vv = (1, 2.300000)
17-
custom = (customize me - no cpp2::to_string overload exists for this type)
17+
custom = std::any
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
the answer
21
(no match)
2+
rev dodgson
3+
(no match)
4+
the answer
5+
zero
6+
plugh
37
zero
48
integer -42
59
xyzzy
10+
(no match)

regression-tests/test-results/gcc-10/mixed-string-interpolation.cpp.execution

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ p = (first, (empty))
1414
t = (3.140000, (empty), (empty))
1515
vv = 0
1616
vv = (1, 2.300000)
17-
custom = (customize me - no cpp2::to_string overload exists for this type)
17+
custom = std::any
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
the answer
1+
zero
2+
rev dodgson
23
(no match)
4+
the answer
5+
zero
6+
plugh
37
zero
48
integer -42
59
xyzzy
10+
(no match)

regression-tests/test-results/msvc-2022/mixed-string-interpolation.cpp.execution

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ p = (first, (empty))
1414
t = (3.140000, (empty), (empty))
1515
vv = 0
1616
vv = (1, 2.300000)
17-
custom = (customize me - no cpp2::to_string overload exists for this type)
17+
custom = std::any
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
the answer
1+
zero
2+
rev dodgson
23
(no match)
4+
the answer
5+
zero
6+
plugh
37
zero
48
integer -42
59
xyzzy
10+
(no match)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pure2-inspect-values.cpp
2+
Creating library test.lib and object test.exp

regression-tests/test-results/pure2-inspect-values.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,32 @@
55

66
#line 1 "pure2-inspect-values.cpp2"
77
[[nodiscard]] auto main() -> int;
8-
#line 9 "pure2-inspect-values.cpp2"
8+
#line 23 "pure2-inspect-values.cpp2"
99
auto test(auto const& x) -> void;
1010

1111
//=== Cpp2 definitions ==========================================================
1212

1313
#line 1 "pure2-inspect-values.cpp2"
1414
[[nodiscard]] auto main() -> int{
15-
test(42);
16-
test(3.14);
15+
std::variant<double,std::string,double> v { };
16+
test(v);
17+
v = "rev dodgson";
18+
test(std::move(v));
19+
20+
std::optional<int> o { };
21+
test(o);
22+
o = 42;
23+
test(std::move(o));
24+
25+
std::any a { 0 };
26+
test(a);
27+
a = cpp2::as< std::string>("plugh");
28+
test(std::move(a));
29+
1730
test(0);
1831
test(-42);
1932
test(cpp2::as< std::string>("xyzzy"));
33+
test(3.14);
2034
}
2135

2236
auto test(auto const& x) -> void{
@@ -25,7 +39,7 @@ auto test(auto const& x) -> void{
2539
if (cpp2::is(__expr, 0)) { if constexpr( requires{"zero";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("zero")),std::string> ) return "zero"; else return std::string{}; else return std::string{}; }
2640
else if (cpp2::is(__expr, (std::move(forty_two)))) { if constexpr( requires{"the answer";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("the answer")),std::string> ) return "the answer"; else return std::string{}; else return std::string{}; }
2741
else if (cpp2::is<int>(__expr)) { if constexpr( requires{"integer " + cpp2::to_string(x);} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("integer " + cpp2::to_string(x))),std::string> ) return "integer " + cpp2::to_string(x); else return std::string{}; else return std::string{}; }
28-
else if (cpp2::is<std::string>(__expr)) { if constexpr( requires{x;} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((x)),std::string> ) return x; else return std::string{}; else return std::string{}; }
42+
else if (cpp2::is<std::string>(__expr)) { if constexpr( requires{cpp2::as<std::string>(x);} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((cpp2::as<std::string>(x))),std::string> ) return cpp2::as<std::string>(x); else return std::string{}; else return std::string{}; }
2943
else return "(no match)"; }
3044
()
3145
<< "\n";}

0 commit comments

Comments
 (0)