Skip to content

Commit 61e685f

Browse files
committed
is(): make is for inspecting variant generic
In the current code cppfront can inspect variants with up to 20 types. This change makes possible to inspect variants with any number of types. The inspection is also done in recursive way.
1 parent 630e61a commit 61e685f

File tree

6 files changed

+338
-100
lines changed

6 files changed

+338
-100
lines changed

include/cpp2util.h

+122-100
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,11 @@ concept specialization_of_template_type_and_nttp = requires (X x) {
649649
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
650650
};
651651

652+
template<typename X>
653+
concept boolean_testable = std::convertible_to<X, bool> && requires(X&& x) {
654+
{ !std::forward<X>(x) } -> std::convertible_to<bool>;
655+
};
656+
652657
template <typename X>
653658
concept dereferencable = requires (X x) { *x; };
654659

@@ -732,6 +737,67 @@ auto pointer_eq(T const* a, T const* b) {
732737
return std::compare_three_way{}(a, b) == std::strong_ordering::equal;
733738
}
734739

740+
//-----------------------------------------------------------------------
741+
//
742+
// A type_find_if for iterating over types in parameter packs
743+
//
744+
// Note: the current implementation is a workaround for clang-12 internal error.
745+
// Original implementation does not need type_it and is implemented
746+
// using lambda with explicit parameter type list in the following way:
747+
//
748+
// template <typename... Ts, typename F>
749+
// constexpr auto type_find_if(F&& fun)
750+
// {
751+
// std::size_t found = std::variant_npos;
752+
// [&]<std::size_t... Is>(std::index_sequence<Is...>){
753+
// if constexpr ((requires { {CPP2_FORWARD(fun).template operator()<Is, Ts>()} -> std::convertible_to<bool>;} && ...)) {
754+
// (((CPP2_FORWARD(fun).template operator()<Is, Ts>()) && (found = Is, true)) || ...);
755+
// }
756+
// }(std::index_sequence_for<Ts...>());
757+
// return found;
758+
// }
759+
//
760+
// The workaround is not needed in gcc-12.1+, clang-13+, msvc 19.29+
761+
//
762+
// Note2: the internal if constexpr could have else with static_assert.
763+
// Unfortunatelly I cannot make it work on MSVC.
764+
//
765+
//-----------------------------------------------------------------------
766+
//
767+
template <std::size_t Index, typename T>
768+
struct type_it {
769+
using type = T;
770+
inline static const std::size_t index = Index;
771+
};
772+
773+
template <typename... Ts, typename F>
774+
constexpr auto type_find_if(F&& fun)
775+
{
776+
std::size_t found = std::variant_npos;
777+
[&]<std::size_t... Is>(std::index_sequence<Is...>){
778+
if constexpr ((requires { {CPP2_FORWARD(fun)(type_it<Is, Ts>{})} -> boolean_testable;} && ...)) {
779+
((CPP2_FORWARD(fun)(type_it<Is, Ts>{}) && (found = Is, true)) || ...);
780+
}
781+
}(std::index_sequence_for<Ts...>());
782+
return found;
783+
}
784+
785+
template <typename F, template<typename...> class C, typename... Ts>
786+
constexpr auto type_find_if(C<Ts...>, F&& fun)
787+
{
788+
return type_find_if<Ts...>(CPP2_FORWARD(fun));
789+
}
790+
791+
template <typename T, typename... Ts>
792+
constexpr auto variant_contains_type(std::variant<Ts...>)
793+
{
794+
if constexpr (is_any<T, Ts...>) {
795+
return std::true_type{};
796+
} else {
797+
return std::false_type{};
798+
}
799+
}
800+
735801

736802
//-----------------------------------------------------------------------
737803
//
@@ -1652,6 +1718,62 @@ auto is( X const& x ) -> bool {
16521718
return Dynamic_cast<C const*>(&x) != nullptr;
16531719
}
16541720
}
1721+
else if constexpr (
1722+
specialization_of_template<X, std::variant>
1723+
)
1724+
{
1725+
/*
1726+
I am adding two versions of the algorithm that check emptiness.
1727+
1728+
Taking into consideration the following code:
1729+
1730+
std::variant<std::monostate,
1731+
std::variant<std::monostate,
1732+
std::variant<std::monostate, int>
1733+
>
1734+
> v;
1735+
1736+
v = std::variant<std::monostate,
1737+
std::variant<std::monostate, int>
1738+
>{
1739+
std::variant<std::monostate, int>{std::monostate{}}
1740+
};
1741+
1742+
If any of this 'std::monostate' is set (regardless of which variant),
1743+
the recursive algorithm will consider it empty.
1744+
This will be returned the same as asking if it contains std::monostate.
1745+
1746+
is<empty>(v) == true;
1747+
is<std::monostate>(v) == true;
1748+
1749+
A non-recursive algorithm will consider it empty only when the top-level variant is set monostable.
1750+
This will make it return a different value than asking if it contains std::monostate.
1751+
1752+
is<empty>(v) == false;
1753+
is<std::monostate>(v) == true;
1754+
1755+
*/
1756+
if (x.valueless_by_exception()) {
1757+
return std::is_same_v<C, empty>;
1758+
}
1759+
if constexpr (
1760+
std::is_same_v<C, empty>
1761+
)
1762+
{
1763+
if constexpr (requires { {variant_contains_type<std::monostate>(std::declval<X>())} -> std::same_as<std::true_type>; }) {
1764+
#if true // recursive check of emptyness (empty when top-level variant contains monostate or when contains variant that is empty)
1765+
if (std::get_if<std::monostate>(&x) != nullptr)
1766+
return true;
1767+
#else // non-recursive check of emptyness (empty only when top-level variant contains monostate)
1768+
return std::get_if<std::monostate>(&x) != nullptr;
1769+
#endif
1770+
}
1771+
}
1772+
return type_find_if(x, [&]<typename It>(It const&) -> bool {
1773+
if (x.index() == It::index) { return is<C>(std::get<It::index>(x));}
1774+
return false;
1775+
}) != std::variant_npos;
1776+
}
16551777
else if constexpr (
16561778
requires { *x; X(); }
16571779
&& std::is_same_v<C, empty>
@@ -1908,106 +2030,6 @@ constexpr auto operator_as( std::variant<Ts...> const& x ) -> decltype(auto) {
19082030
}
19092031

19102032

1911-
// is Type
1912-
//
1913-
template<typename... Ts>
1914-
constexpr auto operator_is( std::variant<Ts...> const& x ) {
1915-
return x.index();
1916-
}
1917-
1918-
template<typename T, typename... Ts>
1919-
auto is( std::variant<Ts...> const& x );
1920-
1921-
1922-
// is Value
1923-
//
1924-
template<typename... Ts>
1925-
constexpr auto is( std::variant<Ts...> const& x, auto&& value ) -> bool
1926-
{
1927-
// Predicate case
1928-
if constexpr (requires{ bool{ value(operator_as< 0>(x)) }; }) { if (x.index() == 0) return value(operator_as< 0>(x)); }
1929-
else if constexpr (requires{ bool{ value(operator_as< 1>(x)) }; }) { if (x.index() == 1) return value(operator_as< 1>(x)); }
1930-
else if constexpr (requires{ bool{ value(operator_as< 2>(x)) }; }) { if (x.index() == 2) return value(operator_as< 2>(x)); }
1931-
else if constexpr (requires{ bool{ value(operator_as< 3>(x)) }; }) { if (x.index() == 3) return value(operator_as< 3>(x)); }
1932-
else if constexpr (requires{ bool{ value(operator_as< 4>(x)) }; }) { if (x.index() == 4) return value(operator_as< 4>(x)); }
1933-
else if constexpr (requires{ bool{ value(operator_as< 5>(x)) }; }) { if (x.index() == 5) return value(operator_as< 5>(x)); }
1934-
else if constexpr (requires{ bool{ value(operator_as< 6>(x)) }; }) { if (x.index() == 6) return value(operator_as< 6>(x)); }
1935-
else if constexpr (requires{ bool{ value(operator_as< 7>(x)) }; }) { if (x.index() == 7) return value(operator_as< 7>(x)); }
1936-
else if constexpr (requires{ bool{ value(operator_as< 8>(x)) }; }) { if (x.index() == 8) return value(operator_as< 8>(x)); }
1937-
else if constexpr (requires{ bool{ value(operator_as< 9>(x)) }; }) { if (x.index() == 9) return value(operator_as< 9>(x)); }
1938-
else if constexpr (requires{ bool{ value(operator_as<10>(x)) }; }) { if (x.index() == 10) return value(operator_as<10>(x)); }
1939-
else if constexpr (requires{ bool{ value(operator_as<11>(x)) }; }) { if (x.index() == 11) return value(operator_as<11>(x)); }
1940-
else if constexpr (requires{ bool{ value(operator_as<12>(x)) }; }) { if (x.index() == 12) return value(operator_as<12>(x)); }
1941-
else if constexpr (requires{ bool{ value(operator_as<13>(x)) }; }) { if (x.index() == 13) return value(operator_as<13>(x)); }
1942-
else if constexpr (requires{ bool{ value(operator_as<14>(x)) }; }) { if (x.index() == 14) return value(operator_as<14>(x)); }
1943-
else if constexpr (requires{ bool{ value(operator_as<15>(x)) }; }) { if (x.index() == 15) return value(operator_as<15>(x)); }
1944-
else if constexpr (requires{ bool{ value(operator_as<16>(x)) }; }) { if (x.index() == 16) return value(operator_as<16>(x)); }
1945-
else if constexpr (requires{ bool{ value(operator_as<17>(x)) }; }) { if (x.index() == 17) return value(operator_as<17>(x)); }
1946-
else if constexpr (requires{ bool{ value(operator_as<18>(x)) }; }) { if (x.index() == 18) return value(operator_as<18>(x)); }
1947-
else if constexpr (requires{ bool{ value(operator_as<19>(x)) }; }) { if (x.index() == 19) return value(operator_as<19>(x)); }
1948-
else if constexpr (std::is_function_v<decltype(value)> || requires{ &value.operator(); }) {
1949-
return false;
1950-
}
1951-
1952-
// Value case
1953-
else {
1954-
if constexpr (requires{ bool{ operator_as< 0>(x) == value }; }) { if (x.index() == 0) return operator_as< 0>(x) == value; }
1955-
if constexpr (requires{ bool{ operator_as< 1>(x) == value }; }) { if (x.index() == 1) return operator_as< 1>(x) == value; }
1956-
if constexpr (requires{ bool{ operator_as< 2>(x) == value }; }) { if (x.index() == 2) return operator_as< 2>(x) == value; }
1957-
if constexpr (requires{ bool{ operator_as< 3>(x) == value }; }) { if (x.index() == 3) return operator_as< 3>(x) == value; }
1958-
if constexpr (requires{ bool{ operator_as< 4>(x) == value }; }) { if (x.index() == 4) return operator_as< 4>(x) == value; }
1959-
if constexpr (requires{ bool{ operator_as< 5>(x) == value }; }) { if (x.index() == 5) return operator_as< 5>(x) == value; }
1960-
if constexpr (requires{ bool{ operator_as< 6>(x) == value }; }) { if (x.index() == 6) return operator_as< 6>(x) == value; }
1961-
if constexpr (requires{ bool{ operator_as< 7>(x) == value }; }) { if (x.index() == 7) return operator_as< 7>(x) == value; }
1962-
if constexpr (requires{ bool{ operator_as< 8>(x) == value }; }) { if (x.index() == 8) return operator_as< 8>(x) == value; }
1963-
if constexpr (requires{ bool{ operator_as< 9>(x) == value }; }) { if (x.index() == 9) return operator_as< 9>(x) == value; }
1964-
if constexpr (requires{ bool{ operator_as<10>(x) == value }; }) { if (x.index() == 10) return operator_as<10>(x) == value; }
1965-
if constexpr (requires{ bool{ operator_as<11>(x) == value }; }) { if (x.index() == 11) return operator_as<11>(x) == value; }
1966-
if constexpr (requires{ bool{ operator_as<12>(x) == value }; }) { if (x.index() == 12) return operator_as<12>(x) == value; }
1967-
if constexpr (requires{ bool{ operator_as<13>(x) == value }; }) { if (x.index() == 13) return operator_as<13>(x) == value; }
1968-
if constexpr (requires{ bool{ operator_as<14>(x) == value }; }) { if (x.index() == 14) return operator_as<14>(x) == value; }
1969-
if constexpr (requires{ bool{ operator_as<15>(x) == value }; }) { if (x.index() == 15) return operator_as<15>(x) == value; }
1970-
if constexpr (requires{ bool{ operator_as<16>(x) == value }; }) { if (x.index() == 16) return operator_as<16>(x) == value; }
1971-
if constexpr (requires{ bool{ operator_as<17>(x) == value }; }) { if (x.index() == 17) return operator_as<17>(x) == value; }
1972-
if constexpr (requires{ bool{ operator_as<18>(x) == value }; }) { if (x.index() == 18) return operator_as<18>(x) == value; }
1973-
if constexpr (requires{ bool{ operator_as<19>(x) == value }; }) { if (x.index() == 19) return operator_as<19>(x) == value; }
1974-
}
1975-
return false;
1976-
}
1977-
1978-
1979-
// as
1980-
//
1981-
template<typename T, typename... Ts>
1982-
auto is( std::variant<Ts...> const& x ) {
1983-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return true; }
1984-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 1>(x)), T >) { if (x.index() == 1) return true; }
1985-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 2>(x)), T >) { if (x.index() == 2) return true; }
1986-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return true; }
1987-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 4>(x)), T >) { if (x.index() == 4) return true; }
1988-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 5>(x)), T >) { if (x.index() == 5) return true; }
1989-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 6>(x)), T >) { if (x.index() == 6) return true; }
1990-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 7>(x)), T >) { if (x.index() == 7) return true; }
1991-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return true; }
1992-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 9>(x)), T >) { if (x.index() == 9) return true; }
1993-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<10>(x)), T >) { if (x.index() == 10) return true; }
1994-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) { if (x.index() == 11) return true; }
1995-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) { if (x.index() == 12) return true; }
1996-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) { if (x.index() == 13) return true; }
1997-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<14>(x)), T >) { if (x.index() == 14) return true; }
1998-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) { if (x.index() == 15) return true; }
1999-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<16>(x)), T >) { if (x.index() == 16) return true; }
2000-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return true; }
2001-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return true; }
2002-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<19>(x)), T >) { if (x.index() == 19) return true; }
2003-
if constexpr (std::is_same_v< T, empty > ) {
2004-
if (x.valueless_by_exception()) return true;
2005-
// Need to guard this with is_any otherwise the get_if is illegal
2006-
if constexpr (is_any<std::monostate, Ts...>) return std::get_if<std::monostate>(&x) != nullptr;
2007-
}
2008-
return false;
2009-
}
2010-
20112033
template<typename T, typename... Ts>
20122034
auto as( std::variant<Ts...> && x ) -> decltype(auto) {
20132035
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return operator_as<0>(x); }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
test: (v) = {
2+
std::cout << "v is empty = (v is void)$" << std::endl;
3+
std::cout << "v is std::monostate = (v is std::monostate)$" << std::endl;
4+
std::cout << "v is X< 0> = (v is X< 0>)$" << std::endl;
5+
std::cout << "v is X<10> = (v is X<10>)$" << std::endl;
6+
std::cout << "v is X<29> = (v is X<29>)$" << std::endl;
7+
std::cout << "v is X<30> = (v is X<30>)$" << std::endl;
8+
std::cout << "v is ThrowingConstruction = (v is ThrowingConstruction)$" << std::endl;
9+
std::cout << std::endl;
10+
}
11+
12+
set_and_test: (inout v, what, forward value) = {
13+
std::cout << "set v to (what)$" << std::endl;
14+
v = value;
15+
test(v);
16+
}
17+
18+
main: () -> int = {
19+
20+
v: std::variant<std::monostate,
21+
ThrowingConstruction,
22+
X< 0>, X< 1>, X< 2>, X< 3>, X< 4>, X< 5>, X< 6>, X< 7>, X< 8>, X< 9>,
23+
X<10>, X<11>, X<12>, X<13>, X<14>, X<15>, X<16>, X<17>, X<18>, X<19>,
24+
PotentiallyThrowingVariant,
25+
X<20>, X<21>, X<22>, X<23>, X<24>, X<25>, X<26>, X<27>, X<28>, X<29>> = ();
26+
27+
28+
set_and_test(v, "std::monostate", std::monostate());
29+
set_and_test(v, "X<19>", X<19>());
30+
set_and_test(v, "X<29>", X<29>());
31+
set_and_test(v, "X<0> in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>", PotentiallyThrowingVariant(X<0>()));
32+
set_and_test(v, "std::monostate in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>", PotentiallyThrowingVariant(std::monostate()));
33+
set_and_test(v, "ThrowingConstruction", ThrowingConstruction());
34+
35+
std::cout << "set v to valueless by exception state" << std::endl;
36+
set_to_valueless_by_exception(v);
37+
test(v);
38+
39+
std::cout << "set v element to valueless by exception state" << std::endl;
40+
v.emplace<22>();
41+
set_to_valueless_by_exception(v.get<22>());
42+
test(v);
43+
}
44+
45+
template<int I>
46+
struct X { operator int() const { return I; } };
47+
48+
struct ThrowingConstruction {
49+
constexpr ThrowingConstruction() = default;
50+
ThrowingConstruction(int) { throw 1; }
51+
};
52+
53+
PotentiallyThrowingVariant: type == std::variant<std::monostate, ThrowingConstruction, X<0>, X<1>, X<2>>;
54+
55+
void set_to_valueless_by_exception(auto& v) try {
56+
v.template emplace<1>(42);
57+
} catch (...) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
set v to std::monostate
2+
v is empty = true
3+
v is std::monostate = true
4+
v is X< 0> = false
5+
v is X<10> = false
6+
v is X<29> = false
7+
v is X<30> = false
8+
v is ThrowingConstruction = false
9+
10+
set v to X<19>
11+
v is empty = false
12+
v is std::monostate = false
13+
v is X< 0> = false
14+
v is X<10> = false
15+
v is X<29> = false
16+
v is X<30> = false
17+
v is ThrowingConstruction = false
18+
19+
set v to X<29>
20+
v is empty = false
21+
v is std::monostate = false
22+
v is X< 0> = false
23+
v is X<10> = false
24+
v is X<29> = true
25+
v is X<30> = false
26+
v is ThrowingConstruction = false
27+
28+
set v to X<0> in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>
29+
v is empty = false
30+
v is std::monostate = false
31+
v is X< 0> = true
32+
v is X<10> = false
33+
v is X<29> = false
34+
v is X<30> = false
35+
v is ThrowingConstruction = false
36+
37+
set v to std::monostate in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>
38+
v is empty = true
39+
v is std::monostate = true
40+
v is X< 0> = false
41+
v is X<10> = false
42+
v is X<29> = false
43+
v is X<30> = false
44+
v is ThrowingConstruction = false
45+
46+
set v to ThrowingConstruction
47+
v is empty = false
48+
v is std::monostate = false
49+
v is X< 0> = false
50+
v is X<10> = false
51+
v is X<29> = false
52+
v is X<30> = false
53+
v is ThrowingConstruction = true
54+
55+
set v to valueless by exception state
56+
v is empty = true
57+
v is std::monostate = false
58+
v is X< 0> = false
59+
v is X<10> = false
60+
v is X<29> = false
61+
v is X<30> = false
62+
v is ThrowingConstruction = false
63+
64+
set v element to valueless by exception state
65+
v is empty = true
66+
v is std::monostate = false
67+
v is X< 0> = false
68+
v is X<10> = false
69+
v is X<29> = false
70+
v is X<30> = false
71+
v is ThrowingConstruction = false
72+

regression-tests/test-results/apple-clang-14-c++2b/mixed-is-with-variant.cpp.output

Whitespace-only changes.

0 commit comments

Comments
 (0)