Skip to content

Commit f5c4d27

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.
1 parent 3a2d11e commit f5c4d27

File tree

6 files changed

+339
-100
lines changed

6 files changed

+339
-100
lines changed

include/cpp2util.h

+123-100
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,11 @@ concept specialization_of_template_type_and_nttp = requires (X x) {
615615
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
616616
};
617617

618+
template<typename X>
619+
concept boolean_testable = std::convertible_to<X, bool> && requires(X&& x) {
620+
{ !std::forward<X>(x) } -> std::convertible_to<bool>;
621+
};
622+
618623
template <typename X>
619624
concept dereferencable = requires (X x) { *x; };
620625

@@ -705,6 +710,67 @@ auto pointer_eq(T const* a, T const* b) {
705710
return std::compare_three_way{}(a, b) == std::strong_ordering::equal;
706711
}
707712

713+
//-----------------------------------------------------------------------
714+
//
715+
// A type_find_if for iterating over types in parameter packs
716+
//
717+
// Note: the current implementation is a workaround for clang-12 internal error.
718+
// Original implementation does not need type_it and is implemented
719+
// using lambda with explicit parameter type list in the following way:
720+
//
721+
// template <typename... Ts, typename F>
722+
// constexpr auto type_find_if(F&& fun)
723+
// {
724+
// std::size_t found = std::variant_npos;
725+
// [&]<std::size_t... Is>(std::index_sequence<Is...>){
726+
// if constexpr ((requires { {CPP2_FORWARD(fun).template operator()<Is, Ts>()} -> std::convertible_to<bool>;} && ...)) {
727+
// (((CPP2_FORWARD(fun).template operator()<Is, Ts>()) && (found = Is, true)) || ...);
728+
// }
729+
// }(std::index_sequence_for<Ts...>());
730+
// return found;
731+
// }
732+
//
733+
// The workaround is not needed in gcc-12.1+, clang-13+, msvc 19.29+
734+
//
735+
// Note2: the internal if constexpr could have else with static_assert.
736+
// Unfortunatelly I cannot make it work on MSVC.
737+
//
738+
//-----------------------------------------------------------------------
739+
//
740+
template <std::size_t Index, typename T>
741+
struct type_it {
742+
using type = T;
743+
inline static const std::size_t index = Index;
744+
};
745+
746+
template <typename... Ts, typename F>
747+
constexpr auto type_find_if(F&& fun)
748+
{
749+
std::size_t found = std::variant_npos;
750+
[&]<std::size_t... Is>(std::index_sequence<Is...>){
751+
if constexpr ((requires { {CPP2_FORWARD(fun)(type_it<Is, Ts>{})} -> boolean_testable;} && ...)) {
752+
((CPP2_FORWARD(fun)(type_it<Is, Ts>{}) && (found = Is, true)) || ...);
753+
}
754+
}(std::index_sequence_for<Ts...>());
755+
return found;
756+
}
757+
758+
template <typename F, template<typename...> class C, typename... Ts>
759+
constexpr auto type_find_if(C<Ts...>, F&& fun)
760+
{
761+
return type_find_if<Ts...>(CPP2_FORWARD(fun));
762+
}
763+
764+
template <typename T, typename... Ts>
765+
constexpr auto variant_contains_type(std::variant<Ts...>)
766+
{
767+
if constexpr (is_any<T, Ts...>) {
768+
return std::true_type{};
769+
} else {
770+
return std::false_type{};
771+
}
772+
}
773+
708774

709775
//-----------------------------------------------------------------------
710776
//
@@ -1625,6 +1691,63 @@ auto is( X const& x ) -> bool {
16251691
return Dynamic_cast<C const*>(&x) != nullptr;
16261692
}
16271693
}
1694+
else if constexpr (
1695+
specialization_of_template<X, std::variant>
1696+
)
1697+
{
1698+
/*
1699+
I am adding two versions of the algorithm that check emptiness.
1700+
1701+
Taking into consideration the following code:
1702+
1703+
std::variant<std::monostate,
1704+
std::variant<std::monostate,
1705+
std::variant<std::monostate, int>
1706+
>
1707+
> v;
1708+
1709+
v = std::variant<std::monostate,
1710+
std::variant<std::monostate, int>
1711+
>{
1712+
std::variant<std::monostate, int>{std::monostate{}}
1713+
};
1714+
1715+
Recursive algorithm will consider it empty if any of these
1716+
std::monostate will be set (no mather in which variant).
1717+
This will be return the same as asking if it contains std::monostate.
1718+
1719+
is<empty>(v) == true;
1720+
is<std::monostate>(v) == true;
1721+
1722+
Non-recursive algorithm will consider it empty only when top-level
1723+
variant will set monostate. This will make it return different
1724+
value then asking if it sontains std::monostate.
1725+
1726+
is<empty>(v) == false;
1727+
is<std::monostate>(v) == true;
1728+
1729+
*/
1730+
if (x.valueless_by_exception()) {
1731+
return std::is_same_v<C, empty>;
1732+
}
1733+
if constexpr (
1734+
std::is_same_v<C, empty>
1735+
)
1736+
{
1737+
if constexpr (requires { {variant_contains_type<std::monostate>(std::declval<X>())} -> std::same_as<std::true_type>; }) {
1738+
#if true // recursive check of emptyness (empty when top-level variant contains monostate or when contains variant that is empty)
1739+
if (std::get_if<std::monostate>(&x) != nullptr)
1740+
return true;
1741+
#else // non-recursive check of emptyness (empty only when top-level variant contains monostate)
1742+
return std::get_if<std::monostate>(&x) != nullptr;
1743+
#endif
1744+
}
1745+
}
1746+
return type_find_if(x, [&]<typename It>(It const&) -> bool {
1747+
if (x.index() == It::index) { return is<C>(std::get<It::index>(x));}
1748+
return false;
1749+
}) != std::variant_npos;
1750+
}
16281751
else if constexpr (
16291752
requires { *x; X(); }
16301753
&& std::is_same_v<C, empty>
@@ -1881,106 +2004,6 @@ constexpr auto operator_as( std::variant<Ts...> const& x ) -> decltype(auto) {
18812004
}
18822005

18832006

1884-
// is Type
1885-
//
1886-
template<typename... Ts>
1887-
constexpr auto operator_is( std::variant<Ts...> const& x ) {
1888-
return x.index();
1889-
}
1890-
1891-
template<typename T, typename... Ts>
1892-
auto is( std::variant<Ts...> const& x );
1893-
1894-
1895-
// is Value
1896-
//
1897-
template<typename... Ts>
1898-
constexpr auto is( std::variant<Ts...> const& x, auto&& value ) -> bool
1899-
{
1900-
// Predicate case
1901-
if constexpr (requires{ bool{ value(operator_as< 0>(x)) }; }) { if (x.index() == 0) return value(operator_as< 0>(x)); }
1902-
else if constexpr (requires{ bool{ value(operator_as< 1>(x)) }; }) { if (x.index() == 1) return value(operator_as< 1>(x)); }
1903-
else if constexpr (requires{ bool{ value(operator_as< 2>(x)) }; }) { if (x.index() == 2) return value(operator_as< 2>(x)); }
1904-
else if constexpr (requires{ bool{ value(operator_as< 3>(x)) }; }) { if (x.index() == 3) return value(operator_as< 3>(x)); }
1905-
else if constexpr (requires{ bool{ value(operator_as< 4>(x)) }; }) { if (x.index() == 4) return value(operator_as< 4>(x)); }
1906-
else if constexpr (requires{ bool{ value(operator_as< 5>(x)) }; }) { if (x.index() == 5) return value(operator_as< 5>(x)); }
1907-
else if constexpr (requires{ bool{ value(operator_as< 6>(x)) }; }) { if (x.index() == 6) return value(operator_as< 6>(x)); }
1908-
else if constexpr (requires{ bool{ value(operator_as< 7>(x)) }; }) { if (x.index() == 7) return value(operator_as< 7>(x)); }
1909-
else if constexpr (requires{ bool{ value(operator_as< 8>(x)) }; }) { if (x.index() == 8) return value(operator_as< 8>(x)); }
1910-
else if constexpr (requires{ bool{ value(operator_as< 9>(x)) }; }) { if (x.index() == 9) return value(operator_as< 9>(x)); }
1911-
else if constexpr (requires{ bool{ value(operator_as<10>(x)) }; }) { if (x.index() == 10) return value(operator_as<10>(x)); }
1912-
else if constexpr (requires{ bool{ value(operator_as<11>(x)) }; }) { if (x.index() == 11) return value(operator_as<11>(x)); }
1913-
else if constexpr (requires{ bool{ value(operator_as<12>(x)) }; }) { if (x.index() == 12) return value(operator_as<12>(x)); }
1914-
else if constexpr (requires{ bool{ value(operator_as<13>(x)) }; }) { if (x.index() == 13) return value(operator_as<13>(x)); }
1915-
else if constexpr (requires{ bool{ value(operator_as<14>(x)) }; }) { if (x.index() == 14) return value(operator_as<14>(x)); }
1916-
else if constexpr (requires{ bool{ value(operator_as<15>(x)) }; }) { if (x.index() == 15) return value(operator_as<15>(x)); }
1917-
else if constexpr (requires{ bool{ value(operator_as<16>(x)) }; }) { if (x.index() == 16) return value(operator_as<16>(x)); }
1918-
else if constexpr (requires{ bool{ value(operator_as<17>(x)) }; }) { if (x.index() == 17) return value(operator_as<17>(x)); }
1919-
else if constexpr (requires{ bool{ value(operator_as<18>(x)) }; }) { if (x.index() == 18) return value(operator_as<18>(x)); }
1920-
else if constexpr (requires{ bool{ value(operator_as<19>(x)) }; }) { if (x.index() == 19) return value(operator_as<19>(x)); }
1921-
else if constexpr (std::is_function_v<decltype(value)> || requires{ &value.operator(); }) {
1922-
return false;
1923-
}
1924-
1925-
// Value case
1926-
else {
1927-
if constexpr (requires{ bool{ operator_as< 0>(x) == value }; }) { if (x.index() == 0) return operator_as< 0>(x) == value; }
1928-
if constexpr (requires{ bool{ operator_as< 1>(x) == value }; }) { if (x.index() == 1) return operator_as< 1>(x) == value; }
1929-
if constexpr (requires{ bool{ operator_as< 2>(x) == value }; }) { if (x.index() == 2) return operator_as< 2>(x) == value; }
1930-
if constexpr (requires{ bool{ operator_as< 3>(x) == value }; }) { if (x.index() == 3) return operator_as< 3>(x) == value; }
1931-
if constexpr (requires{ bool{ operator_as< 4>(x) == value }; }) { if (x.index() == 4) return operator_as< 4>(x) == value; }
1932-
if constexpr (requires{ bool{ operator_as< 5>(x) == value }; }) { if (x.index() == 5) return operator_as< 5>(x) == value; }
1933-
if constexpr (requires{ bool{ operator_as< 6>(x) == value }; }) { if (x.index() == 6) return operator_as< 6>(x) == value; }
1934-
if constexpr (requires{ bool{ operator_as< 7>(x) == value }; }) { if (x.index() == 7) return operator_as< 7>(x) == value; }
1935-
if constexpr (requires{ bool{ operator_as< 8>(x) == value }; }) { if (x.index() == 8) return operator_as< 8>(x) == value; }
1936-
if constexpr (requires{ bool{ operator_as< 9>(x) == value }; }) { if (x.index() == 9) return operator_as< 9>(x) == value; }
1937-
if constexpr (requires{ bool{ operator_as<10>(x) == value }; }) { if (x.index() == 10) return operator_as<10>(x) == value; }
1938-
if constexpr (requires{ bool{ operator_as<11>(x) == value }; }) { if (x.index() == 11) return operator_as<11>(x) == value; }
1939-
if constexpr (requires{ bool{ operator_as<12>(x) == value }; }) { if (x.index() == 12) return operator_as<12>(x) == value; }
1940-
if constexpr (requires{ bool{ operator_as<13>(x) == value }; }) { if (x.index() == 13) return operator_as<13>(x) == value; }
1941-
if constexpr (requires{ bool{ operator_as<14>(x) == value }; }) { if (x.index() == 14) return operator_as<14>(x) == value; }
1942-
if constexpr (requires{ bool{ operator_as<15>(x) == value }; }) { if (x.index() == 15) return operator_as<15>(x) == value; }
1943-
if constexpr (requires{ bool{ operator_as<16>(x) == value }; }) { if (x.index() == 16) return operator_as<16>(x) == value; }
1944-
if constexpr (requires{ bool{ operator_as<17>(x) == value }; }) { if (x.index() == 17) return operator_as<17>(x) == value; }
1945-
if constexpr (requires{ bool{ operator_as<18>(x) == value }; }) { if (x.index() == 18) return operator_as<18>(x) == value; }
1946-
if constexpr (requires{ bool{ operator_as<19>(x) == value }; }) { if (x.index() == 19) return operator_as<19>(x) == value; }
1947-
}
1948-
return false;
1949-
}
1950-
1951-
1952-
// as
1953-
//
1954-
template<typename T, typename... Ts>
1955-
auto is( std::variant<Ts...> const& x ) {
1956-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return true; }
1957-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 1>(x)), T >) { if (x.index() == 1) return true; }
1958-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 2>(x)), T >) { if (x.index() == 2) return true; }
1959-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return true; }
1960-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 4>(x)), T >) { if (x.index() == 4) return true; }
1961-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 5>(x)), T >) { if (x.index() == 5) return true; }
1962-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 6>(x)), T >) { if (x.index() == 6) return true; }
1963-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 7>(x)), T >) { if (x.index() == 7) return true; }
1964-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return true; }
1965-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 9>(x)), T >) { if (x.index() == 9) return true; }
1966-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<10>(x)), T >) { if (x.index() == 10) return true; }
1967-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) { if (x.index() == 11) return true; }
1968-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) { if (x.index() == 12) return true; }
1969-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) { if (x.index() == 13) return true; }
1970-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<14>(x)), T >) { if (x.index() == 14) return true; }
1971-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) { if (x.index() == 15) return true; }
1972-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<16>(x)), T >) { if (x.index() == 16) return true; }
1973-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return true; }
1974-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return true; }
1975-
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<19>(x)), T >) { if (x.index() == 19) return true; }
1976-
if constexpr (std::is_same_v< T, empty > ) {
1977-
if (x.valueless_by_exception()) return true;
1978-
// Need to guard this with is_any otherwise the get_if is illegal
1979-
if constexpr (is_any<std::monostate, Ts...>) return std::get_if<std::monostate>(&x) != nullptr;
1980-
}
1981-
return false;
1982-
}
1983-
19842007
template<typename T, typename... Ts>
19852008
auto as( std::variant<Ts...> && x ) -> decltype(auto) {
19862009
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)