Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
visit.hpp
Go to the documentation of this file.
1// Copyright 2026, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_VARIANT_VISIT_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_VARIANT_VISIT_HPP_
7
8// IWYU pragma: private, include "arene/base/variant.hpp"
9// IWYU pragma: friend "(arene/base(?!/tests)|stdlib/include/stdlib_detail)/.*"
10
11// parasoft-begin-suppress AUTOSAR-A16_2_2-a "Arene Base aggregate headers permitted by A16-2-2 Permit #1"
12#include "arene/base/contracts/contract.hpp"
13#include "arene/base/functional/invoke.hpp"
14#include "arene/base/stdlib_choice/cstddef.hpp"
15#include "arene/base/stdlib_choice/decay.hpp"
16#include "arene/base/stdlib_choice/declval.hpp"
17#include "arene/base/stdlib_choice/forward.hpp"
18#include "arene/base/stdlib_choice/ignore.hpp"
19#include "arene/base/stdlib_choice/integer_sequence.hpp"
20#include "arene/base/stdlib_choice/is_constructible.hpp"
21#include "arene/base/stdlib_choice/is_same.hpp"
22#include "arene/base/type_traits/all_of.hpp"
23#include "arene/base/type_traits/is_invocable.hpp"
24#include "arene/base/variant/traits.hpp"
25#include "arene/base/variant/variant.hpp" // IWYU pragma: keep
26// parasoft-end-suppress AUTOSAR-A16_2_2-a
27
28namespace arene {
29namespace base {
30
31namespace visit_detail {
32
33/// @brief Trait to determine the return type of invoking a visitor with a given alternative of a variant.
34///
35/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
36/// @tparam VariantRefT The type of the variant. Should include cref qualification.
37/// @tparam Idx The index of the alternative to consider
38template <typename VisitorRefT, typename VariantRefT, std::size_t Idx>
39using visitor_invoke_result_at_t =
40 invoke_result_t<VisitorRefT, decltype(::arene::base::get<Idx>(std::declval<VariantRefT>()))>;
41
42/// @brief Trait to determine what the return type of invoking a visitor on a variant is.
43///
44/// Equivalent to @c visitor_invoke_result_at_t<VisitorT,VariantT,0U> since all overloads must return the same type.
45///
46/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
47/// @tparam VariantRefT The type of the variant. Should include cref qualification.
48template <typename VisitorRefT, typename VariantRefT>
49using visitor_invoke_result_t = visitor_invoke_result_at_t<VisitorRefT, VariantRefT, 0U>;
50
51/// @brief Trait to determine if a visitor has the same return type when invoked with all alternatives of a variant.
52///
53/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
54/// @tparam VariantRefT The type of the variant. Should include cref qualification.
55/// @tparam Indexes an index sequence the size of VariantT
56template <
57 typename VisitorRefT,
58 typename VariantRefT,
59 typename Indexes = std::make_index_sequence<::arene::base::variant_size_v<std::decay_t<VariantRefT>>>>
60class are_visitor_returns_the_same_type;
61
62/// @brief Empty variant specialization of trait to determine if a visitor has the same return type when invoked with
63/// all alternatives of a variant.
64///
65/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
66/// @tparam VariantRefT The type of the variant. Should include cref qualification.
67template <typename VisitorRefT, typename VariantRefT>
68class are_visitor_returns_the_same_type<VisitorRefT, VariantRefT, std::index_sequence<>> {};
69
70/// @brief Trait to determine if a visitor has the same return type when invoked with all alternatives of a variant.
71///
72/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
73/// @tparam VariantRefT The type of the variant. Should include cref qualification.
74/// @tparam Idxs The set of indexs for alternatives in @c VariantT
75template <typename VisitorRefT, typename VariantRefT, std::size_t Idx, std::size_t... Idxs>
76class are_visitor_returns_the_same_type<VisitorRefT, VariantRefT, std::index_sequence<Idx, Idxs...>>
77 : are_visitor_returns_the_same_type<VisitorRefT, VariantRefT, std::index_sequence<Idxs...>> {
78 static_assert(
79 std::is_same<
80 visitor_invoke_result_t<VisitorRefT, VariantRefT>,
81 visitor_invoke_result_at_t<VisitorRefT, VariantRefT, Idx>>::value,
82 "visit requires all visitor overloads to return exactly the same type."
83 );
84};
85
86/// @brief Trait to determine if a visitor is noexcept when invoked with a given alternative of a variant and the return
87/// is coerced into a given type.
88///
89/// @tparam Ret The return type to coerce to.
90/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
91/// @tparam VariantRefT The type of the variant. Should include cref qualification.
92/// @tparam Idx The index of the alternative to consider
93/// @return bool The result of
94/// @c is_nothrow_invocable_v<VisitorT,decltype(::arene::base::get<Idx>(std::declval<VariantT>()))> .
95template <typename Ret, typename VisitorRefT, typename VariantRefT, std::size_t Idx>
96constexpr bool is_visitor_nothrow_invocable_r_at_v =
97 is_nothrow_invocable_r_v<Ret, VisitorRefT, decltype(::arene::base::get<Idx>(std::declval<VariantRefT>()))> &&
98 std::is_nothrow_constructible<Ret, visitor_invoke_result_at_t<VisitorRefT, VariantRefT, Idx>>::value;
99
100/// @brief Trait to determine if a visitor is noexcept when invoked with all alternatives of a variant and coerced to a
101/// given return type.
102///
103/// @tparam Ret The return type to coerce to.
104/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
105/// @tparam VariantRefT The type of the variant. Should include cref qualification.
106/// @tparam Indexes an index sequence the size of VariantT
107template <
108 typename Ret,
109 typename VisitorRefT,
110 typename VariantRefT,
111 typename Indexes = std::make_index_sequence<::arene::base::variant_size_v<std::decay_t<VariantRefT>>>>
112constexpr bool is_visitor_nothrow_invocable_r_v{false};
113
114/// @brief Trait to determine if a visitor is noexcept when invoked with all alternatives of a variant and coerced to a
115/// given return type.
116///
117/// @tparam Ret The return type to coerce to.
118/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
119/// @tparam VariantRefT The type of the variant. Should include cref qualification.
120/// @tparam Idxs The set of indexs for alternatives in @c VariantT
121template <typename Ret, typename VisitorRefT, typename VariantRefT, std::size_t... Idxs>
122constexpr bool is_visitor_nothrow_invocable_r_v<
123 Ret,
124 VisitorRefT,
125 VariantRefT,
126 std::index_sequence<Idxs...>>{::arene::base::all_of_v<
127 is_visitor_nothrow_invocable_r_at_v<Ret, VisitorRefT, VariantRefT, Idxs>...>};
128
129/// @brief Helper for mapping visitor overloads to variant alternatives and dispatching via a jump table.
130///
131/// @tparam VisitorRefT The type of the visitor. Should include cref qualification.
132/// @tparam VariantRefT The type of the variant. Should include cref qualification.
133/// @tparam Indices An index sequence the size of VariantT
134template <
135 typename VisitorRefT,
136 typename VariantRefT,
137 typename RetT = visitor_invoke_result_t<VisitorRefT, VariantRefT>,
138 typename Indexes = std::make_index_sequence<::arene::base::variant_size_v<std::decay_t<VariantRefT>>>>
139class visit_dispatcher;
140
141/// @brief Helper for mapping visitor overloads to variant alternatives and dispatching via a jump table.
142///
143/// @tparam VisitorT The type of the visitor. Should include cref qualification.
144/// @tparam VariantT The type of the variant. Should include cref qualification.
145/// @tparam Idxs The set of indexs for alternatives in @c VariantT
146template <typename VisitorRefT, typename VariantRefT, typename RetT, std::size_t... Idxs>
147class visit_dispatcher<VisitorRefT, VariantRefT, RetT, std::index_sequence<Idxs...>> {
148 /// @brief Trampoline function which invokes the visitor using the value at the given alternative in the variant.
149 ///
150 /// @tparam Idx The index of the alternative in the variant to invoke visitor with.
151 /// @param visitor The visitor to invoke with the variant.
152 /// @param vrnt The variant to invoke the visitor with.
153 /// @invariant @c holds_alternative<Idx>(vrnt)==true .
154 /// @return return_type The result of invoking the variant with the given alternative.
155 // parasoft-begin-suppress AUTOSAR-A8_4_6-a "False positive, the static_cast is equivalent to move."
156 // parasoft-begin-suppress AUTOSAR-A8_4_5-a "False positive, the static_cast is equivalent to move."
157 // parasoft-begin-suppress AUTOSAR-A12_8_4-a "False positive, the static_cast is equivalent to move."
158 template <std::size_t Idx>
159 static constexpr auto invoke_visitor(
160 VisitorRefT visitor,
161 VariantRefT vrnt
162 ) noexcept(is_visitor_nothrow_invocable_r_at_v<RetT, VisitorRefT, VariantRefT, Idx>) -> RetT {
163 return ::arene::base::invoke(
164 static_cast<VisitorRefT>(visitor),
165 ::arene::base::get<Idx>(static_cast<VariantRefT>(vrnt))
166 );
167 }
168 // parasoft-end-suppress AUTOSAR-A8_4_6-a "False positive, the static_cast is equivalent to move."
169 // parasoft-end-suppress AUTOSAR-A8_4_5-a "False positive, the static_cast is equivalent to move."
170 // parasoft-end-suppress AUTOSAR-A12_8_4-a "False positive, the static_cast is equivalent to move."
171
172 /// @brief The signature of a trampoline function for invoking a visitor with a variant.
173 using invoke_visitor_fn_t = RetT (*)(VisitorRefT visitor, VariantRefT vrnt);
174 /// @brief The type of a jump table of trampoline functions.
175 // NOLINTNEXTLINE(hicpp-avoid-c-arrays) Better codegen, and compile-time invariants prevent oob access.
176 using jump_table_t = invoke_visitor_fn_t[::arene::base::variant_size_v<std::decay_t<VariantRefT>>];
177
178 /// @brief The jump table of trampoline functions. The entry at @c Idx is @c invoke_visitor<Idx> .
179 // parasoft-begin-suppress AUTOSAR-A18_1_1-a "Better codegen, and compile-time invariants prevent oob access."
180 static constexpr jump_table_t visitor_fns{invoke_visitor<Idxs>...};
181 // parasoft-end-suppress AUTOSAR-A18_1_1-a
182
183 public:
184 /// @brief Invokes the given visitor with the active alternative in the given variant.
185 ///
186 /// @param visitor The visitor to invoke with the variant.
187 /// @param vrnt The variant to invoke the visitor with.
188 /// @return return_type Equivalent to @c invoke_visitor<Idx>(visitor,variant) where @c Idx is equal to
189 /// @c variant.index() .
190 // parasoft-begin-suppress AUTOSAR-A8_4_6-a "False positive, the static_cast is equivalent to move."
191 // parasoft-begin-suppress AUTOSAR-A8_4_5-a "False positive, the static_cast is equivalent to move."
192 // parasoft-begin-suppress AUTOSAR-A12_8_4-a "False positive, the static_cast is equivalent to move."
193 constexpr auto operator()(VisitorRefT visitor, VariantRefT vrnt) const
194 noexcept(is_visitor_nothrow_invocable_r_v<RetT, VisitorRefT, VariantRefT>) -> RetT {
195 return visitor_fns[vrnt.index()](static_cast<VisitorRefT>(visitor), static_cast<VariantRefT>(vrnt));
196 }
197 // parasoft-end-suppress AUTOSAR-A12_8_4-a
198 // parasoft-end-suppress AUTOSAR-A8_4_5-a
199 // parasoft-end-suppress AUTOSAR-A8_4_6-a
200};
201
202template <typename VisitorRefT, typename VariantRefT, typename RetT, std::size_t... Idxs>
203constexpr typename visit_dispatcher<VisitorRefT, VariantRefT, RetT, std::index_sequence<Idxs...>>::jump_table_t
204 visit_dispatcher<VisitorRefT, VariantRefT, RetT, std::index_sequence<Idxs...>>::visitor_fns;
205
206} // namespace visit_detail
207
208/// @brief Invokes a given visitor with the active alternative in a given variant.
209///
210/// @tparam VisitorT The type of the visitor to invoke.
211/// @tparam VariantT The type of the variant to unwrap. Given @c variant<Ts...> , @c is_invocable<VisitorT,Ts> must hold
212/// where @c Visitor and @c Ts maintain the cref qualification of Ts.
213/// @param visitor The visitor to invoke.
214/// @param vrnt The variant to take the active alternative of.
215/// @return The result of invoking @c visitor with the active alternative in @c vrnt .
216/// @pre @c vrnt.valueless_by_exception()==false , else @c ARENE_PRECONDITION violation.
217// parasoft-begin-suppress AUTOSAR-M3_3_2-a "False positive, this is not a member function and thus cannot be static.""
218template <typename VisitorT, typename VariantT>
219auto visit(VisitorT&& visitor, VariantT&& vrnt) noexcept(visit_detail::is_visitor_nothrow_invocable_r_v<
221 visitor_invoke_result_t<decltype(visitor), decltype(vrnt)>,
222 decltype(visitor),
223 decltype(vrnt)>)
224 -> visit_detail::visitor_invoke_result_t<decltype(visitor), decltype(vrnt)> {
225 // We do this indirection to get better compiler messages if this condition is violated; the user sees the actual
226 // overload that violates the constraint. The validation struct has to be assigned to ignore to avoid
227 // discard-of-return static analysis warnings.
228 std::ignore = visit_detail::are_visitor_returns_the_same_type<decltype(visitor), decltype(vrnt)>{};
229 ARENE_PRECONDITION(!vrnt.valueless_by_exception());
230 return visit_detail::visit_dispatcher<decltype(visitor), decltype(vrnt)>{
231 }(std::forward<VisitorT>(visitor), std::forward<VariantT>(vrnt));
232}
233// parasoft-end-suppress AUTOSAR-M3_3_2-a "False positive, this is not a member function and thus cannot be static."
234
235/// @brief Invokes a given visitor with the active alternative in a given variant.
236///
237/// @tparam RetT The type to coerce all return types to.
238/// @tparam VisitorT The type of the visitor to invoke.
239/// @tparam VariantT The type of the variant to unwrap. Given @c variant<Ts...> , @c is_invocable_r<RetT,VisitorT,Ts>
240/// must hold where @c Visitor and @c Ts maintain the cref qualification @c visitor and @c variant
241/// respectively.
242/// @param visitor The visitor to invoke.
243/// @param vrnt The variant to take the active alternative of.
244/// @return RetT The result of invoking @c visitor with the active alternative in @c vrnt , converted to @c RetT .
245/// @pre @c vrnt.valueless_by_exception()==false , else @c ARENE_PRECONDITION violation.
246// parasoft-begin-suppress AUTOSAR-M3_3_2-a "False positive, this is not a member function and thus cannot be static."
247template <typename RetT, typename VisitorT, typename VariantT>
248auto visit(
249 VisitorT&& visitor,
250 VariantT&& vrnt
251) noexcept(visit_detail::is_visitor_nothrow_invocable_r_v<RetT, decltype(visitor), decltype(vrnt)>) -> RetT {
252 ARENE_PRECONDITION(!vrnt.valueless_by_exception());
253 return visit_detail::visit_dispatcher<decltype(visitor), decltype(vrnt), RetT>{
254 }(std::forward<VisitorT>(visitor), std::forward<VariantT>(vrnt));
255}
256// parasoft-end-suppress AUTOSAR-M3_3_2-a "False positive, this is not a member function and thus cannot be static."
257
258} // namespace base
259} // namespace arene
260
261#endif // INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_VARIANT_VISIT_HPP_
Definition array_exceptions_disabled.cpp:11
auto visit(VisitorT &&visitor, VariantT &&vrnt) noexcept(visit_detail::is_visitor_nothrow_invocable_r_v< visit_detail::visitor_invoke_result_t<, >,, >) -> visit_detail::visitor_invoke_result_t<, >
Invokes a given visitor with the active alternative in a given variant.
Definition visit.hpp:219
auto visit(VisitorT &&visitor, VariantT &&vrnt) noexcept(visit_detail::is_visitor_nothrow_invocable_r_v< RetT,, >) -> RetT
Invokes a given visitor with the active alternative in a given variant.
Definition visit.hpp:248
Copyright 2026, Toyota Motor Corporation.
Definition array_exceptions_disabled.cpp:10