// OpenVPN -- An application to securely tunnel IP networks // over a single port, with support for SSL/TLS-based // session authentication and key exchange, // packet encryption, packet authentication, and // packet compression. // // Copyright (C) 2012-2017 OpenVPN Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License Version 3 // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program in the COPYING file. // If not, see . // High-performance functor with move-only semantics. #ifndef OPENVPN_COMMON_FUNCTION_H #define OPENVPN_COMMON_FUNCTION_H #include // for std::size_t #include // for std::move #include #include namespace openvpn { // F -- function type (usually a lambda expression) // N (default=3) -- max size of functor in void* machine words before we overflow to dynamic allocation // INTERN_ONLY (default=false) -- if true, throw a static assertion if functor cannot be stored internally template class Function; template class Function { public: Function() noexcept { methods = nullptr; } template Function(T&& functor) noexcept { construct(std::move(functor)); } Function(Function&& other) noexcept { methods = other.methods; other.methods = nullptr; if (methods) methods->move(data, other.data); } Function& operator=(Function&& other) noexcept { if (methods) methods->destruct(data); methods = other.methods; other.methods = nullptr; if (methods) methods->move(data, other.data); return *this; } ~Function() { if (methods) methods->destruct(data); } template void reset(T&& functor) noexcept { if (methods) methods->destruct(data); construct(std::move(functor)); } void reset() noexcept { if (methods) { methods->destruct(data); methods = nullptr; } } R operator()(A... args) const { return methods->invoke(data, std::forward(args)...); } explicit operator bool() const noexcept { return methods != nullptr; } private: #ifdef _MSC_VER template void construct(T&& functor) noexcept { constexpr bool is_intern = (sizeof(Intern) <= sizeof(data)); static_assert(!INTERN_ONLY || is_intern, "Function: Intern doesn't fit in data[] and INTERN_ONLY=true"); static_assert(sizeof(Extern) <= sizeof(data), "Function: Extern doesn't fit in data[]"); if (is_intern) { // store functor internally (in data) setup_methods_intern(); new (data) Intern(std::move(functor)); } else { // store functor externally (using new) setup_methods_extern(); new (data) Extern(std::move(functor)); } } #else template static constexpr bool is_intern() { return sizeof(Intern) <= sizeof(data); } template (), int>::type = 0> void construct(T&& functor) noexcept { // store functor internally (in data) setup_methods_intern(); new (data) Intern(std::move(functor)); } template (), int>::type = 0> void construct(T&& functor) noexcept { static_assert(!INTERN_ONLY, "Function: Intern doesn't fit in data[] and INTERN_ONLY=true"); static_assert(sizeof(Extern) <= sizeof(data), "Function: Extern doesn't fit in data[]"); // store functor externally (using new) setup_methods_extern(); new (data) Extern(std::move(functor)); } #endif struct Methods { R (*invoke)(void *, A&&...); void (*move)(void *, void *); void (*destruct)(void *); }; template void setup_methods_intern() { static const struct Methods m = { &Intern::invoke, &Intern::move, &Intern::destruct, }; methods = &m; } template void setup_methods_extern() { static const struct Methods m = { &Extern::invoke, &Extern::move, &Extern::destruct, }; methods = &m; } // store functor internally (in data) template class Intern { public: Intern(T&& functor) noexcept : functor_(std::move(functor)) { } static R invoke(void* ptr, A&&... args) { Intern* self = reinterpret_cast(ptr); return self->functor_(std::forward(args)...); } static void move(void *dest, void *src) { Intern* s = reinterpret_cast(src); new (dest) Intern(std::move(*s)); } static void destruct(void *ptr) { Intern* self = reinterpret_cast(ptr); self->~Intern(); } private: T functor_; }; // store functor externally (using new) template class Extern { public: Extern(T&& functor) noexcept : functor_(new T(std::move(functor))) { } static R invoke(void* ptr, A&&... args) { Extern* self = reinterpret_cast(ptr); return (*self->functor_)(std::forward(args)...); } static void move(void *dest, void *src) { Extern* d = reinterpret_cast(dest); Extern* s = reinterpret_cast(src); d->functor_ = s->functor_; // no need to set s->functor_=nullptr because parent will not destruct src after move } static void destruct(void *ptr) { Extern* self = reinterpret_cast(ptr); delete self->functor_; } private: T* functor_; }; const Methods* methods; mutable void* data[N]; }; } #endif