Skip to content

Commit 6c1d14d

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 6c1d14d

16 files changed

+1228
-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...> const&, 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 false // 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,58 @@
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 << "v is PotentiallyThrowingVariant = (v is PotentiallyThrowingVariant)$" << std::endl;
10+
std::cout << std::endl;
11+
}
12+
13+
set_and_test: (inout v, what, forward value) = {
14+
std::cout << "set v to (what)$" << std::endl;
15+
v = value;
16+
test(v);
17+
}
18+
19+
main: () -> int = {
20+
21+
v: std::variant<std::monostate,
22+
ThrowingConstruction,
23+
X< 0>, X< 1>, X< 2>, X< 3>, X< 4>, X< 5>, X< 6>, X< 7>, X< 8>, X< 9>,
24+
X<10>, X<11>, X<12>, X<13>, X<14>, X<15>, X<16>, X<17>, X<18>, X<19>,
25+
PotentiallyThrowingVariant,
26+
X<20>, X<21>, X<22>, X<23>, X<24>, X<25>, X<26>, X<27>, X<28>, X<29>> = ();
27+
28+
29+
set_and_test(v, "std::monostate", std::monostate());
30+
set_and_test(v, "X<19>", X<19>());
31+
set_and_test(v, "X<29>", X<29>());
32+
set_and_test(v, "X<0> in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>", PotentiallyThrowingVariant(X<0>()));
33+
set_and_test(v, "std::monostate in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>", PotentiallyThrowingVariant(std::monostate()));
34+
set_and_test(v, "ThrowingConstruction", ThrowingConstruction());
35+
36+
std::cout << "set v to valueless by exception state" << std::endl;
37+
set_to_valueless_by_exception(v);
38+
test(v);
39+
40+
std::cout << "set v element to valueless by exception state" << std::endl;
41+
v.emplace<22>();
42+
set_to_valueless_by_exception(v.get<22>());
43+
test(v);
44+
}
45+
46+
template<int I>
47+
struct X { operator int() const { return I; } };
48+
49+
struct ThrowingConstruction {
50+
constexpr ThrowingConstruction() = default;
51+
ThrowingConstruction(int) { throw 1; }
52+
};
53+
54+
PotentiallyThrowingVariant: type == std::variant<std::monostate, ThrowingConstruction, X<0>, X<1>, X<2>>;
55+
56+
void set_to_valueless_by_exception(auto& v) try {
57+
v.template emplace<1>(42);
58+
} catch (...) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
v is PotentiallyThrowingVariant = false
10+
11+
set v to X<19>
12+
v is empty = false
13+
v is std::monostate = false
14+
v is X< 0> = false
15+
v is X<10> = false
16+
v is X<29> = false
17+
v is X<30> = false
18+
v is ThrowingConstruction = false
19+
v is PotentiallyThrowingVariant = false
20+
21+
set v to X<29>
22+
v is empty = false
23+
v is std::monostate = false
24+
v is X< 0> = false
25+
v is X<10> = false
26+
v is X<29> = true
27+
v is X<30> = false
28+
v is ThrowingConstruction = false
29+
v is PotentiallyThrowingVariant = false
30+
31+
set v to X<0> in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>
32+
v is empty = false
33+
v is std::monostate = false
34+
v is X< 0> = true
35+
v is X<10> = false
36+
v is X<29> = false
37+
v is X<30> = false
38+
v is ThrowingConstruction = false
39+
v is PotentiallyThrowingVariant = true
40+
41+
set v to std::monostate in std::variant<std::monostate, PotentiallyThrowingVariant, X<0>, X<1>, X<2>>
42+
v is empty = false
43+
v is std::monostate = true
44+
v is X< 0> = false
45+
v is X<10> = false
46+
v is X<29> = false
47+
v is X<30> = false
48+
v is ThrowingConstruction = false
49+
v is PotentiallyThrowingVariant = true
50+
51+
set v to ThrowingConstruction
52+
v is empty = false
53+
v is std::monostate = false
54+
v is X< 0> = false
55+
v is X<10> = false
56+
v is X<29> = false
57+
v is X<30> = false
58+
v is ThrowingConstruction = true
59+
v is PotentiallyThrowingVariant = false
60+
61+
set v to valueless by exception state
62+
v is empty = true
63+
v is std::monostate = false
64+
v is X< 0> = false
65+
v is X<10> = false
66+
v is X<29> = false
67+
v is X<30> = false
68+
v is ThrowingConstruction = false
69+
v is PotentiallyThrowingVariant = false
70+
71+
set v element to valueless by exception state
72+
v is empty = false
73+
v is std::monostate = false
74+
v is X< 0> = false
75+
v is X<10> = false
76+
v is X<29> = false
77+
v is X<30> = false
78+
v is ThrowingConstruction = false
79+
v is PotentiallyThrowingVariant = true
80+

0 commit comments

Comments
 (0)