Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
function_ref.hpp
Go to the documentation of this file.
1// Copyright 2024, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_FUNCTIONAL_FUNCTION_REF_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_FUNCTIONAL_FUNCTION_REF_HPP_
7
8// IWYU pragma: private, include "arene/base/functional.hpp"
9// IWYU pragma: friend "(arene/base(?!/tests)|stdlib/include/stdlib_detail)/.*"
10
11// parasoft-begin-suppress AUTOSAR-A16_2_2-a-2 "Arene Base aggregate headers permitted by A16-2-2 Permit #1"
12#include "arene/base/array/array.hpp"
13#include "arene/base/byte/byte.hpp"
14#include "arene/base/constraints/constraints.hpp"
15#include "arene/base/contracts/contract.hpp"
16#include "arene/base/stdlib_choice/addressof.hpp"
17#include "arene/base/stdlib_choice/enable_if.hpp"
18#include "arene/base/stdlib_choice/forward.hpp"
19#include "arene/base/stdlib_choice/ignore.hpp"
20#include "arene/base/stdlib_choice/is_function.hpp"
21#include "arene/base/stdlib_choice/is_fundamental.hpp"
22#include "arene/base/stdlib_choice/is_same.hpp"
23#include "arene/base/stdlib_choice/is_trivially_copyable.hpp"
24#include "arene/base/stdlib_choice/is_void.hpp"
25#include "arene/base/stdlib_choice/max_value_overload.hpp"
26#include "arene/base/stdlib_choice/memcpy.hpp"
27#include "arene/base/stdlib_choice/remove_cv.hpp"
28#include "arene/base/stdlib_choice/remove_reference.hpp"
29#include "arene/base/type_traits/conditional.hpp"
30#include "arene/base/type_traits/is_invocable.hpp"
31#include "arene/base/type_traits/remove_cvref.hpp"
32#include "arene/base/type_traits/type_identity.hpp"
33// parasoft-end-suppress AUTOSAR-A16_2_2-a-2
34
35// parasoft-begin-suppress CERT_C-EXP37-b-3 "False positive: The rule does not mention naming all parameters"
36// parasoft-begin-suppress AUTOSAR-M2_10_1-a-2 "Similar names permitted by M2-10-1 Permit #1"
37
38namespace arene {
39namespace base {
40
41/// @brief An implementation of the proposed <c>std::function_ref</c> from [P0792](https://wg21.link/p0792). Provides a
42/// type-erased callable like <c>std::function</c>, but just stores a reference to the wrapped callable rather than a
43/// copy of it.
44/// @tparam Signature The function signature being wrapped of the form <c>Return-type (Args...) optional-const</c>
45/// @tparam IsNoexcept If the generated call operator should be noexcept
46template <typename Signature, bool IsNoexcept = false>
47class function_ref;
48
49/// @brief A convience alias for @c function_ref with @c IsNoexcept set @c true
50/// @tparam Signature The function signature being wrapped of the form <c>Return-type (Args...) optional-const</c>
51template <typename Signature>
52using noexcept_function_ref = function_ref<Signature, true>;
53
54namespace function_ref_detail {
55/// @brief Crash with a precondition violation if the supplied pointer is null, or dereference it if not
56/// @tparam Func The function type being pointed to
57/// @param ptr A function pointer
58/// @return A reference to the object pointed to by @c ptr
59/// @pre @c ptr is not null, otherwise @c ARENE_PRECONDITION violation
60template <typename Func>
61auto checked_deref(Func* ptr) noexcept -> Func& {
62 ARENE_PRECONDITION(ptr != nullptr);
63 return *ptr;
64}
65
66/// @brief The type to use for passing an argument to the @c function_ref trampoline
67/// function: the type itself for fundamental types and small
68/// trivial types, otherwise a reference to the argument
69/// @tparam The type of the argument
70template <typename T>
71using arg_passing_type = conditional_t<
72 std::is_fundamental<T>::value || (std::is_trivially_copyable<T>::value && sizeof(T) <= sizeof(void*)),
73 T,
74 T&>;
75
76// parasoft-begin-suppress AUTOSAR-A12_1_5-a-2 "False positive: delegating constructors are used"
77/// @brief The base class for @c function_ref that provides the implementation.
78/// @tparam IsConst Is the @c Signature of the @c function_ref @c const qualified?
79/// @tparam IsNoexcept Should the generated call operator be marked @c noexcept ?
80/// @tparam R The return type
81/// @tparam Args The types of the function call arguments
82template <bool IsConst, bool IsNoexcept, typename R, typename... Args>
83class function_ref_base {
84 /// @brief The type of the buffer for storing the pointer. Assumes all function
85 /// pointers have the same size.
86 using storage_buffer = arene::base::array<byte, std::max(sizeof(void*), sizeof(void (*)()))>;
87
88 /// @brief The type of the stored function to invoke the wrapped callable
89 using call_type = R (*)(storage_buffer, arg_passing_type<Args>...);
90
91 static_assert(sizeof(call_type) == sizeof(void (*)()), "Function pointers must have the same size");
92
93 /// @brief Type alias to const-qualify the supplied type @c F if @c IsConst
94 /// @tparam F the type of the callable object
95 template <typename F>
96 using func_type = conditional_t<IsConst, F const, F>;
97
98 /// @brief Invoke a wrapped callable object or function of type @c F with the
99 /// supplied arguments, where @c R is not @c void.
100 /// @tparam F The type of the wrapped object
101 /// @tparam ReturnType An alias for @c R to allow the constraint
102 /// @param buffer A reference to the storage buffer
103 /// @param args The supplied arguments for the call
104 /// @return The result of invoking the wrapped callable object with the
105 /// supplied arguments.
106 template <
107 typename F,
108 typename ReturnType = R,
109 constraints<std::enable_if_t<!std::is_void<ReturnType>::value>> = nullptr>
110 static auto invoke_wrapped_callable(storage_buffer buffer, arg_passing_type<Args>... args) -> R {
111 // parasoft-begin-suppress AUTOSAR-A7_1_5-a-2 "False positive: not declared with an auto specifier"
112 F* local_f{nullptr};
113 // parasoft-end-suppress AUTOSAR-A7_1_5-a-2
114 std::ignore = std::memcpy(&local_f, buffer.data(), sizeof(local_f));
115 // parasoft-begin-suppress AUTOSAR-A5_2_2-a-2 "False positive: C-style cast not used"
116 return static_cast<R>((*local_f)(static_cast<Args&&>(args)...));
117 // parasoft-end-suppress AUTOSAR-A5_2_2-a-2
118 }
119
120 /// @brief Invoke a wrapped callable object or function of type @c F with the
121 /// supplied arguments, where @c R is @c void.
122 /// @tparam F The type of the wrapped object
123 /// @tparam ReturnType An alias for @c R to allow the constraint
124 /// @param buffer A reference to the storage buffer
125 /// @param args The supplied arguments for the call
126 template <
127 typename F,
128 typename ReturnType = R,
129 constraints<std::enable_if_t<std::is_void<ReturnType>::value>> = nullptr>
130 static void invoke_wrapped_callable(storage_buffer buffer, arg_passing_type<Args>... args) {
131 // parasoft-begin-suppress AUTOSAR-A7_1_5-a-2 "False positive: not declared with an auto specifier"
132 F* local_f{nullptr};
133 // parasoft-end-suppress AUTOSAR-A7_1_5-a-2
134 std::ignore = std::memcpy(&local_f, buffer.data(), sizeof(local_f));
135 static_cast<void>((*local_f)(static_cast<Args&&>(args)...));
136 }
137
138 /// @brief Internal constructor to reduce duplication. Copies a non-owning pointer to the argument.
139 /// @tparam PointeeType the type of the pointed-to entity to be stored
140 /// @tparam CallType the type to cast the pointee to when invoking
141 /// @param f_ref A reference to the function to store
142 template <typename PointeeType, typename CallType>
143 function_ref_base(PointeeType const& f_ref, type_identity<CallType>) noexcept
144 : call_(&invoke_wrapped_callable<CallType>) {
145 // parasoft-begin-suppress AUTOSAR-A7_1_5-a "False positive: 'f_pointer' is not declared with 'auto'
146 PointeeType const* f_pointer{std::addressof(f_ref)};
147 // parasoft-end-suppress AUTOSAR-A7_1_5-a
148 std::ignore = std::memcpy(buffer_.data(), &f_pointer, sizeof(f_pointer));
149 }
150
151 /// @brief Trait to determine if a callable is invocable with matching constness, noexcept, return and arguments.
152 /// @tparam F the function type to test.
153 /// @return If @c IsNoexcept , then equivalent to @c is_nothrow_invocable_r<R,F,Args...> . Otherwise,
154 /// equivalent to @c is_invocable_r<R,F,Args...>
155 template <typename F>
156 static constexpr bool
157 is_invocable_n_r_v{IsNoexcept ? ::arene::base::is_nothrow_invocable_r_v<R, F, Args...> : ::arene::base::is_invocable_r_v<R, F, Args...>};
158
159 public:
160 // parasoft-begin-suppress CERT_C-EXP37-a "False-positive: All arguments are named"
161 /// @brief Invoke the wrapped callable with the specified arguments
162 /// @param args The arguments to pass to the callable
163 /// @return The result of the invocation of the wrapped callable
164 auto operator()(Args... args) const noexcept(IsNoexcept) -> R {
165 // NOLINTNEXTLINE(bugprone-exception-escape) noexcept isn't captured in the type signature. Invariants ensure it is.
166 return call_(buffer_, args...);
167 }
168 // parasoft-end-suppress CERT_C-EXP37-a
169
170 // parasoft-begin-suppress AUTOSAR-A8_4_6-a-2 "This class stores a reference, so does not copy or move data"
171 // parasoft-begin-suppress AUTOSAR-A8_4_5-a-2 "This class stores a reference, so does not copy or move data"
172 // parasoft-begin-suppress AUTOSAR-A12_8_4-a-2 "This class stores a reference, so does not copy or move data"
173 /// @brief Construct a @c function_ref from a callable object. If @c IsNoexcept , must satisfy
174 /// @c is_nothrow_invocable_r<R,F,Args...> , otherwise @c is_invocable_r<R,F,Args...>
175 /// @tparam F The deduced type of the supplied callable object or a reference to it
176 /// @param func A reference to the supplied callable object
177 /// @note This takes a forwarding @c F&& reference in order to account for cases where @c F is overloaded, so the
178 /// current @c function_ref specialization can select the overload which matches its CV qualification. @c func is
179 /// always copied, not moved from, even when it's deduced as an rvalue reference.
180 template <
181 typename F,
182 constraints<
183 std::enable_if_t<!std::is_same<function_ref_base, remove_cvref_t<F>>::value>,
184 std::enable_if_t<!std::is_function<std::remove_reference_t<F>>::value>,
185 std::enable_if_t<is_invocable_n_r_v<F>>> = nullptr>
186 // AUTOSAR A12-1-4 exception. We *want* @c function_ref to be implicitly
187 // convertible from callable objects that can be invoked with the appropriate
188 // signature, in order to facilitate the use of @c function_ref as a function
189 // argument type.
190 // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions,bugprone-forwarding-reference-overload)
191 function_ref_base(F&& func) noexcept
192 : function_ref_base(std::forward<F>(func), type_identity<func_type<std::remove_reference_t<F>>>{}) {}
193 // parasoft-end-suppress AUTOSAR-A8_4_6-a-2
194 // parasoft-end-suppress AUTOSAR-A8_4_5-a-2
195 // parasoft-end-suppress AUTOSAR-A12_8_4-a-2
196
197 // parasoft-begin-suppress AUTOSAR-A13_3_1-a-2 "False positive: Constrained via SFINAE"
198 /// @brief Construct a @c function_ref from a function pointer.
199 /// @tparam F The deduced type of the supplied function
200 /// @param func A pointer to the supplied function
201 /// @pre Requires <c>f(args...)</c> is a valid expression (where each element
202 /// of @c args has the type of the corresponding element of @c Args), and
203 /// convertible to @c R
204 /// @pre @c func must not be <c>nullptr</c>, otherwise @c ARENE_PRECONDITION violation
205 template <
206 typename F,
207 constraints<std::enable_if_t<std::is_function<F>::value>, std::enable_if_t<is_invocable_n_r_v<F>>> = nullptr>
208 // AUTOSAR A12-1-4 exception. We *want* @c function_ref to be implicitly
209 // convertible from functions that can be invoked with the appropriate
210 // signature, in order to facilitate the use of @c function_ref as a function
211 // argument type.
212 // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
213 function_ref_base(F* func) noexcept
214 : function_ref_base(function_ref_detail::checked_deref(func), type_identity<F>{}) {}
215
216 protected:
217 /// @brief default copy constructor
218 /// @param other The source
219 function_ref_base(function_ref_base const& other) = default;
220 /// @brief default move constructor
221 /// @param other The source
222 function_ref_base(function_ref_base&& other) = default;
223 // parasoft-end-suppress AUTOSAR-A13_3_1-a-2
224
225 /// @brief Default destructor
226 ~function_ref_base() = default;
227
228 /// @brief default copy assignment
229 /// @param other The source
230 auto operator=(function_ref_base const& other) -> function_ref_base& = default;
231 /// @brief default move assignment
232 /// @param other The source
233 auto operator=(function_ref_base&& other) -> function_ref_base& = default;
234
235 private:
236 /// @brief The buffer for storing the pointer.
237 alignas(void*) alignas(call_type) storage_buffer buffer_{};
238 /// @brief Pointer to the trampoline function which will invoke the wrapped function
239 /// or callable object
240 call_type call_;
241};
242// parasoft-end-suppress AUTOSAR-A12_1_5-a-2
243
244} // namespace function_ref_detail
245
246/// @brief This specialization handles non-const-qualified function signatures.
247/// @tparam R The return type for the function call operator
248/// @tparam Args The types of the arguments for the function call operator
249/// @tparam IsNoexcept If @c true , the function will have a @c noexcept qualified call operator. Otherwise, it will
250/// not.
251template <typename R, typename... Args, bool IsNoexcept>
252class function_ref<R(Args...), IsNoexcept> : function_ref_detail::function_ref_base<false, IsNoexcept, R, Args...> {
253 /// @brief Convenience typedef for the base class
254 using base_class = function_ref_detail::function_ref_base<false, IsNoexcept, R, Args...>;
255
256 public:
257 /// Expose the base class constructors
258 using base_class::base_class;
259 /// Expose the base class function call operator
260 using base_class::operator();
261};
262
263/// @cond INTERNAL
264/// @brief This specialization handles const-qualified function signatures.
265/// @tparam R The return type for the function call operator
266/// @tparam Args The types of the arguments for the function call operator
267/// @tparam IsNoexcept If @c true , the function will have a @c noexcept qualified call operator. Otherwise, it will
268/// not.
269template <typename R, typename... Args, bool IsNoexcept>
270class function_ref<R(Args...) const, IsNoexcept>
271 : function_ref_detail::function_ref_base<true, IsNoexcept, R, Args...> {
272 /// @brief Convenience typedef for the base class
273 using base_class = function_ref_detail::function_ref_base<true, IsNoexcept, R, Args...>;
274
275 public:
276 /// @brief Expose the base class constructors
277 using base_class::base_class;
278 /// @brief Expose the base class function call operator
279 using base_class::operator();
280};
281/// @endcond
282
283} // namespace base
284} // namespace arene
285
286// parasoft-end-suppress AUTOSAR-M2_10_1-a-2
287
288#endif // INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_FUNCTIONAL_FUNCTION_REF_HPP_
Definition array_exceptions_disabled.cpp:11
Copyright 2026, Toyota Motor Corporation.
Definition array_exceptions_disabled.cpp:10