OpenVPN 3 Core Library
Loading...
Searching...
No Matches
function.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// High-performance functor with move-only semantics.
13
14#ifndef OPENVPN_COMMON_FUNCTION_H
15#define OPENVPN_COMMON_FUNCTION_H
16
17#include <cstddef> // for std::size_t
18#include <utility> // for std::move
19#include <type_traits>
20#include <new>
21
22namespace openvpn {
23// F -- function type (usually a lambda expression)
24// N (default=3) -- max size of functor in void* machine words before we overflow to dynamic allocation
25// INTERN_ONLY (default=false) -- if true, throw a static assertion if functor cannot be stored internally
26template <typename F, std::size_t N = 3, bool INTERN_ONLY = false>
28
29template <typename R, typename... A, std::size_t N, bool INTERN_ONLY>
30class Function<R(A...), N, INTERN_ONLY>
31{
32 public:
33 Function() noexcept
34 {
35 methods = nullptr;
36 }
37
38 template <typename T>
39 Function(T &&functor) noexcept
40 {
41 construct(std::move(functor));
42 }
43
44 Function(Function &&other) noexcept
45 {
46 methods = other.methods;
47 other.methods = nullptr;
48 if (methods)
49 methods->move(data, other.data);
50 }
51
52 Function &operator=(Function &&other) noexcept
53 {
54 if (methods)
55 methods->destruct(data);
56 methods = other.methods;
57 other.methods = nullptr;
58 if (methods)
59 methods->move(data, other.data);
60 return *this;
61 }
62
64 {
65 if (methods)
66 methods->destruct(data);
67 }
68
69 template <typename T>
70 void reset(T &&functor) noexcept
71 {
72 if (methods)
73 methods->destruct(data);
74 construct(std::move(functor));
75 }
76
77 void reset() noexcept
78 {
79 if (methods)
80 {
81 methods->destruct(data);
82 methods = nullptr;
83 }
84 }
85
86 R operator()(A... args) const
87 {
88 return methods->invoke(data, std::forward<A>(args)...);
89 }
90
91 explicit operator bool() const noexcept
92 {
93 return methods != nullptr;
94 }
95
96 private:
97#ifdef _MSC_VER
98 template <typename T>
99 void construct(T &&functor) noexcept
100 {
101 constexpr bool is_intern = (sizeof(Intern<T>) <= sizeof(data));
102 static_assert(!INTERN_ONLY || is_intern, "Function: Intern<T> doesn't fit in data[] and INTERN_ONLY=true");
103 static_assert(sizeof(Extern<T>) <= sizeof(data), "Function: Extern<T> doesn't fit in data[]");
104
105 if (is_intern)
106 {
107 // store functor internally (in data)
108 setup_methods_intern<T>();
109 new (data) Intern<T>(std::move(functor));
110 }
111 else
112 {
113 // store functor externally (using new)
114 setup_methods_extern<T>();
115 new (data) Extern<T>(std::move(functor));
116 }
117 }
118#else
119 template <typename T>
120 static constexpr bool is_intern()
121 {
122 return sizeof(Intern<T>) <= sizeof(data);
123 }
124
125 template <typename T,
126 typename std::enable_if<is_intern<T>(), int>::type = 0>
127 void construct(T &&functor) noexcept
128 {
129 // store functor internally (in data)
130 setup_methods_intern<T>();
131 new (data) Intern<T>(std::move(functor));
132 }
133
134 template <typename T,
135 typename std::enable_if<!is_intern<T>(), int>::type = 0>
136 void construct(T &&functor) noexcept
137 {
138 static_assert(!INTERN_ONLY, "Function: Intern<T> doesn't fit in data[] and INTERN_ONLY=true");
139 static_assert(sizeof(Extern<T>) <= sizeof(data), "Function: Extern<T> doesn't fit in data[]");
140
141 // store functor externally (using new)
142 setup_methods_extern<T>();
143 new (data) Extern<T>(std::move(functor));
144 }
145#endif
146
147 struct Methods
148 {
149 R (*invoke)(void *, A &&...);
150 void (*move)(void *, void *);
151 void (*destruct)(void *);
152 };
153
154 template <typename T>
156 {
157 static const struct Methods m = {
158 &Intern<T>::invoke,
159 &Intern<T>::move,
160 &Intern<T>::destruct,
161 };
162 methods = &m;
163 }
164
165 template <typename T>
167 {
168 static const struct Methods m = {
169 &Extern<T>::invoke,
170 &Extern<T>::move,
171 &Extern<T>::destruct,
172 };
173 methods = &m;
174 }
175
176 // store functor internally (in data)
177 template <typename T>
178 class Intern
179 {
180 public:
181 Intern(T &&functor) noexcept
182 : functor_(std::move(functor))
183 {
184 }
185
186 static R invoke(void *ptr, A &&...args)
187 {
188 Intern *self = reinterpret_cast<Intern *>(ptr);
189 return self->functor_(std::forward<A>(args)...);
190 }
191
192 static void move(void *dest, void *src)
193 {
194 Intern *s = reinterpret_cast<Intern *>(src);
195 new (dest) Intern(std::move(*s));
196 }
197
198 static void destruct(void *ptr)
199 {
200 Intern *self = reinterpret_cast<Intern *>(ptr);
201 self->~Intern();
202 }
203
204 private:
206 };
207
208 // store functor externally (using new)
209 template <typename T>
210 class Extern
211 {
212 public:
213 Extern(T &&functor) noexcept
214 : functor_(new T(std::move(functor)))
215 {
216 }
217
218 static R invoke(void *ptr, A &&...args)
219 {
220 Extern *self = reinterpret_cast<Extern *>(ptr);
221 return (*self->functor_)(std::forward<A>(args)...);
222 }
223
224 static void move(void *dest, void *src)
225 {
226 Extern *d = reinterpret_cast<Extern *>(dest);
227 Extern *s = reinterpret_cast<Extern *>(src);
228 d->functor_ = s->functor_;
229 // no need to set s->functor_=nullptr because parent will not destruct src after move
230 }
231
232 static void destruct(void *ptr)
233 {
234 Extern *self = reinterpret_cast<Extern *>(ptr);
235 delete self->functor_;
236 }
237
238 private:
240 };
241
242 const Methods *methods;
243 mutable void *data[N];
244};
245} // namespace openvpn
246
247#endif
static void move(void *dest, void *src)
Definition function.hpp:224
static void move(void *dest, void *src)
Definition function.hpp:192
Function(Function &&other) noexcept
Definition function.hpp:44
Function & operator=(Function &&other) noexcept
Definition function.hpp:52