Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.html linguist-vendored
67 changes: 34 additions & 33 deletions doc/rvariant.adoc

Large diffs are not rendered by default.

278 changes: 144 additions & 134 deletions doc/rvariant.html

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions include/iris/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,10 @@
# endif
#endif

// ------------------------------------------------------

#if defined(__RESHARPER__) && !defined(__cpp_lib_reference_from_temporary)
#define __cpp_lib_reference_from_temporary 202202L
#endif

#endif
9 changes: 4 additions & 5 deletions include/iris/rvariant/detail/variant_storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
#include <iris/rvariant/detail/rvariant_fwd.hpp>
#include <iris/rvariant/variant_helper.hpp>

#include <functional>
#include <type_traits>
#include <utility>

#include <cassert>
#include <cstddef> // IWYU pragma: keep

#if defined(_MSC_VER)
# define IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN \
Expand Down Expand Up @@ -298,15 +297,15 @@ template<std::size_t I, class Storage>
else if constexpr (I == 29) return std::forward<Storage>(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.first;
else if constexpr (I == 30) return std::forward<Storage>(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.first;
else if constexpr (I == 31) return std::forward<Storage>(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.first;
else if constexpr (I < 64) return raw_get<I - 32>(
else if constexpr (I < 64) return detail::raw_get<I - 32>(
std::forward<Storage>(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest);
else return raw_get<I - 64>(
else return detail::raw_get<I - 64>(
std::forward<Storage>(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest
.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest);
}

template<std::size_t I, class Storage>
using raw_get_t = decltype(raw_get<I>(std::declval<Storage>()));
using raw_get_t = decltype(detail::raw_get<I>(std::declval<Storage>()));

// --------------------------------------------------
// --------------------------------------------------
Expand Down
95 changes: 39 additions & 56 deletions include/iris/rvariant/detail/visit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <type_traits>

#include <cstddef>

#include <cassert>

namespace iris {

Expand Down Expand Up @@ -71,11 +71,10 @@ using raw_visit_result_t = decltype(std::declval<Visitor>()(
template<std::size_t I, class Visitor, class Storage>
struct raw_visit_noexcept
{
template<class Storage_>
struct lazy_invoke
{
static constexpr std::size_t RealI = detail::valueless_unbias<Storage_>(I);
using type = std::is_nothrow_invocable<
static constexpr std::size_t RealI = detail::valueless_unbias<Storage>(I);
using type = is_nothrow_directly_invocable<
Visitor,
std::in_place_index_t<RealI>,
raw_get_t<RealI, Storage>
Expand All @@ -84,12 +83,12 @@ struct raw_visit_noexcept

static constexpr bool value = std::conditional_t<
!std::remove_cvref_t<Storage>::never_valueless && I == 0,
std::type_identity<std::is_nothrow_invocable<
std::type_identity<is_nothrow_directly_invocable<
Visitor,
std::in_place_index_t<std::variant_npos>,
decltype(std::forward_like<Storage>(std::declval<valueless_t>()))
Storage
>>,
lazy_invoke<Storage>
lazy_invoke
>::type::value;
};

Expand All @@ -109,11 +108,11 @@ do_raw_visit(Visitor&& vis, Storage&& storage) // NOLINT(cppcoreguidelines-rval
noexcept(raw_visit_noexcept<I, Visitor, Storage>::value)
{
if constexpr (!std::remove_cvref_t<Storage>::never_valueless && I == 0) {
return std::invoke(std::forward<Visitor>(vis), std::in_place_index<std::variant_npos>, std::forward<Storage>(storage));
return std::forward<Visitor>(vis)(std::in_place_index<std::variant_npos>, std::forward<Storage>(storage));

} else {
constexpr std::size_t RealI = valueless_unbias<Storage>(I);
return std::invoke(std::forward<Visitor>(vis), std::in_place_index<RealI>, raw_get<RealI>(std::forward<Storage>(storage)));
return std::forward<Visitor>(vis)(std::in_place_index<RealI>, raw_get<RealI>(std::forward<Storage>(storage)));
}
}

Expand Down Expand Up @@ -171,7 +170,7 @@ struct raw_visit_dispatch<NeverValueless, -1>
{
constexpr auto const& table = raw_visit_table<Visitor, Storage>::table;
auto const& f = table[i];
return std::invoke(f, std::forward<Visitor>(vis), std::forward<Storage>(storage));
return f(std::forward<Visitor>(vis), std::forward<Storage>(storage));
}
};

Expand Down Expand Up @@ -238,7 +237,9 @@ raw_visit(Variant&& v, Visitor&& vis) // NOLINT(cppcoreguidelines-missing-std-f
noexcept(raw_visit_noexcept_all<Visitor, forward_storage_t<Variant>>)
{
constexpr std::size_t N = detail::valueless_bias<Variant>(iris::variant_size_v<std::remove_reference_t<Variant>>);
return raw_visit_dispatch<std::remove_cvref_t<Variant>::never_valueless, visit_strategy<N>>::template apply<N>(
return raw_visit_dispatch<std::remove_cvref_t<Variant>::never_valueless, visit_strategy<N>>::template apply<
N, Visitor, forward_storage_t<Variant>
>(
detail::valueless_bias<Variant>(v.index_),
std::forward<Visitor>(vis),
detail::forward_storage<Variant>(v)
Expand All @@ -251,7 +252,9 @@ raw_visit_i(std::size_t const biased_i, Variant&& v, Visitor&& vis) // NOLINT(c
noexcept(raw_visit_noexcept_all<Visitor, forward_storage_t<Variant>>)
{
constexpr std::size_t N = detail::valueless_bias<Variant>(iris::variant_size_v<std::remove_reference_t<Variant>>);
return raw_visit_dispatch<std::remove_cvref_t<Variant>::never_valueless, visit_strategy<N>>::template apply<N>(
return raw_visit_dispatch<std::remove_cvref_t<Variant>::never_valueless, visit_strategy<N>>::template apply<
N, Visitor, forward_storage_t<Variant>
>(
biased_i,
std::forward<Visitor>(vis),
detail::forward_storage<Variant>(v)
Expand All @@ -261,20 +264,11 @@ raw_visit_i(std::size_t const biased_i, Variant&& v, Visitor&& vis) // NOLINT(c

// --------------------------------------------------

// `std::invoke_result_t` MUST NOT be used here due to its side effects:
// <https://eel.is/c++draft/meta.trans.other#tab:meta.trans.other-row-11-column-2-note-2>
// In the case of `variant`, this is not a theoretical concern:
// it has observable consequences where `visit(...)` may be incorrectly
// instantiated during the invocation of `visit<R>(...)`.
// This likely explains why [variant.visit](https://eel.is/c++draft/variant.visit#6)
// explicitly requires using `decltype(e(m))` instead of `std::invoke_result_t`.
// Also, we can't wrap this into a `struct` as it becomes not SFINAE-friendly.

template<class Visitor, class... Variants>
using visit_result_t = decltype(std::invoke( // If you see an error here, your `T0` is not eligible for the `Visitor`.
std::declval<Visitor>(),
unwrap_recursive(detail::raw_get<0>(forward_storage<Variants>(std::declval<Variants>())))...
));
using visit_result_t = std::invoke_result_t< // If you see an error here, your `T0` is not eligible for the `Visitor`.
Visitor,
decltype(unwrap_recursive(detail::raw_get<0>(detail::forward_storage<Variants>(std::declval<Variants>()))))...
>;

template<class T0R, class Visitor, class ArgsList, class... Variants>
struct visit_check_impl;
Expand All @@ -284,19 +278,13 @@ struct visit_check_impl<T0R, Visitor, type_list<Args...>>
{
static constexpr bool accepts_all_alternatives = std::is_invocable_v<Visitor, Args...>;

template<class Visitor_, class... Args_>
struct lazy_invoke
{
using type = decltype(std::invoke(std::declval<Visitor_>(), std::declval<Args_>()...));
};

// In case of `accepts_all_alternatives == false`, this
// intentionally reports false-positive `true` to avoid
// two `static_assert` errors.
static constexpr bool same_return_type = std::is_same_v<
typename std::conditional_t<
accepts_all_alternatives,
lazy_invoke<Visitor, Args...>,
std::invoke_result<Visitor, Args...>,
std::type_identity<T0R>
>::type,
T0R
Expand All @@ -308,16 +296,16 @@ struct visit_check_impl<T0R, Visitor, type_list<Args...>>

template<class T0R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_check_impl<T0R, Visitor, type_list<Args...>, rvariant<Ts...>&, Rest...>
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_type<Ts>&>, Rest...>...> {};
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_t<Ts>&>, Rest...>...> {};
template<class T0R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_check_impl<T0R, Visitor, type_list<Args...>, rvariant<Ts...> const&, Rest...>
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_type<Ts> const&>, Rest...>...> {};
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_t<Ts> const&>, Rest...>...> {};
template<class T0R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_check_impl<T0R, Visitor, type_list<Args...>, rvariant<Ts...>&&, Rest...>
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_type<Ts>>, Rest...>...> {};
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_t<Ts>>, Rest...>...> {};
template<class T0R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_check_impl<T0R, Visitor, type_list<Args...>, rvariant<Ts...> const&&, Rest...>
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_type<Ts> const>, Rest...>...> {};
: std::conjunction<visit_check_impl<T0R, Visitor, type_list<Args..., unwrap_recursive_t<Ts> const>, Rest...>...> {};

template<class T0R, class Visitor, class... Variants>
using visit_check = visit_check_impl<T0R, Visitor, type_list<>, Variants...>;
Expand All @@ -334,20 +322,13 @@ struct visit_R_check_impl<R, Visitor, type_list<Args...>>
// mutually exclusive in order to provide better errors.
static constexpr bool accepts_all_alternatives = std::is_invocable_v<Visitor, Args...>;

template<class Visitor_, class... Args_>
struct lazy_invoke
{
// This is NOT `std::invoke_r`; we need the plain type for conversion check
using type = decltype(std::invoke(std::declval<Visitor_>(), std::declval<Args_>()...));
};

// In case of `accepts_all_alternatives == false`, this
// intentionally reports false-positive `true` to avoid
// two `static_assert` errors.
static constexpr bool return_type_convertible_to_R = std::is_convertible_v<
static constexpr bool return_type_convertible_to_R = iris::detail::invoke_convertible<
typename std::conditional_t<
accepts_all_alternatives,
lazy_invoke<Visitor, Args...>,
std::invoke_result<Visitor, Args...>,
std::type_identity<R>
>::type,
R
Expand All @@ -359,16 +340,16 @@ struct visit_R_check_impl<R, Visitor, type_list<Args...>>

template<class R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_R_check_impl<R, Visitor, type_list<Args...>, rvariant<Ts...>&, Rest...>
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_type<Ts>&>, Rest...>...> {};
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_t<Ts>&>, Rest...>...> {};
template<class R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_R_check_impl<R, Visitor, type_list<Args...>, rvariant<Ts...> const&, Rest...>
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_type<Ts> const&>, Rest...>...> {};
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_t<Ts> const&>, Rest...>...> {};
template<class R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_R_check_impl<R, Visitor, type_list<Args...>, rvariant<Ts...>&&, Rest...>
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_type<Ts>>, Rest...>...> {};
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_t<Ts>>, Rest...>...> {};
template<class R, class Visitor, class... Args, class... Ts, class... Rest>
struct visit_R_check_impl<R, Visitor, type_list<Args...>, rvariant<Ts...> const&&, Rest...>
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_type<Ts> const>, Rest...>...> {};
: std::conjunction<visit_R_check_impl<R, Visitor, type_list<Args..., unwrap_recursive_t<Ts> const>, Rest...>...> {};

template<class R, class Visitor, class... Variants>
using visit_R_check = visit_R_check_impl<R, Visitor, type_list<>, Variants...>;
Expand All @@ -383,14 +364,13 @@ template<class R, std::size_t... Is, class Visitor, class... Storage>
struct multi_visit_noexcept<R, std::index_sequence<Is...>, Visitor, Storage...>
{
private:
template<class... Storage_>
struct lazy_invoke
{
using type = std::is_nothrow_invocable_r<
R,
Visitor,
unwrap_recursive_type<
detail::raw_get_t<detail::valueless_unbias<Storage_>(Is), Storage_>
unwrap_recursive_t<
detail::raw_get_t<detail::valueless_unbias<Storage>(Is), Storage>
>...
>;
};
Expand All @@ -404,7 +384,7 @@ struct multi_visit_noexcept<R, std::index_sequence<Is...>, Visitor, Storage...>
>...
>,
std::type_identity<std::bool_constant<false>>, // throw std::bad_variant_access{};
lazy_invoke<Storage...>
lazy_invoke
>::type::value;
};

Expand Down Expand Up @@ -449,7 +429,8 @@ struct visit_table<
Storage...
>
{
using function_type = R(*)(Visitor&&, Storage&&...);
using function_type = R(*)(Visitor&&, Storage&&...)
noexcept(multi_visit_noexcept<R, type_list<OverloadSeq...>, Visitor, Storage...>::value);

static constexpr function_type table[] = {
&multi_visitor<OverloadSeq>::template apply<R, Visitor, Storage...>...
Expand All @@ -468,7 +449,7 @@ struct visit_dispatch<-1>
{
constexpr auto const& table = visit_table<R, OverloadSeq, Visitor, Storage...>::table;
auto const& f = table[flat_i];
return std::invoke_r<R>(f, std::forward<Visitor>(vis), std::forward<Storage>(storage)...);
return f(std::forward<Visitor>(vis), std::forward<Storage>(storage)...);
}
};

Expand Down Expand Up @@ -588,7 +569,9 @@ struct visit_impl<
std::remove_cvref_t<as_variant_t<Variants>>::never_valueless...
>::get(vars.index_...);

return visit_dispatch<visit_strategy<OverloadSeq::size>>::template apply<R, OverloadSeq>(
return visit_dispatch<visit_strategy<OverloadSeq::size>>::template apply<
R, OverloadSeq, Visitor, forward_storage_t<as_variant_t<Variants>>...
>(
flat_i, std::forward<Visitor>(vis), forward_storage<as_variant_t<Variants>>(vars)...
);
}
Expand Down
Loading
Loading