/************************************************************************* * * Copyright 2019 Realm Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **************************************************************************/ #ifndef REALM_UTIL_FUNCTION_REF_HPP #define REALM_UTIL_FUNCTION_REF_HPP #include <functional> #include <utility> namespace realm::util { template <typename Signature> class UniqueFunction; #ifdef _WIN32 // VC++ warns about multiple copy constructors, but we want both const and // non-const version to ensure they're a better match than the wrapping // constructor. We could instead use enable_if to make the wrapping constructor // ineligible, but that tends to do bad things to compile times. #pragma warning(push) #pragma warning(disable : 4521 4522) #endif /// A lightweight non-owning reference to a callable. /// /// This type is similar to std::function, but unlike std::function holds a reference to the callable rather than /// making a copy of it. This means that it will never require a heap allocation, and produces significantly smaller /// binaries due to the template machinery being much simpler. This type should only ever be used as a function /// parameter that is not stored past when the function returns. All other uses (including trying to store it in a /// std::function) are very unlikely to be correct. /// /// This implements a subset of P0792R5, which hopefully will be incorportated into a future version of the standard /// library. template <typename Signature> class FunctionRef; template <typename Return, typename... Args> class FunctionRef<Return(Args...)> { public: using ThisType = FunctionRef<Return(Args...)>; // A FunctionRef is never empty, and so cannot be default-constructed. constexpr FunctionRef() noexcept = delete; // FunctionRef is copyable and moveable. #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__) FunctionRef(ThisType&) noexcept = default; FunctionRef(ThisType const&) noexcept = default; ThisType& operator=(ThisType&) noexcept = default; ThisType& operator=(const ThisType&) noexcept = default; FunctionRef(ThisType&&) noexcept = default; ThisType& operator=(ThisType&&) noexcept = default; #else #ifdef _WIN32 // VC++ incorrectly rejects multiple versions of a defaulted special member function constexpr FunctionRef(ThisType& o) noexcept : m_obj(o.m_obj) , m_callback(o.m_callback) { } constexpr ThisType& operator=(ThisType& o) noexcept { m_obj = o.m_obj; m_callback = o.m_callback; return *this; } constexpr FunctionRef(ThisType const& o) noexcept : m_obj(o.m_obj) , m_callback(o.m_callback) { } constexpr ThisType& operator=(ThisType const& o) noexcept { m_obj = o.m_obj; m_callback = o.m_callback; return *this; } #else constexpr FunctionRef(ThisType&) noexcept = default; constexpr ThisType& operator=(ThisType&) noexcept = default; constexpr FunctionRef(ThisType const&) noexcept = default; constexpr ThisType& operator=(const ThisType&) noexcept = default; #endif constexpr FunctionRef(ThisType&&) noexcept = default; constexpr ThisType& operator=(ThisType&&) noexcept = default; #endif // Construct a FunctionRef which wraps the given callable. template <typename F> constexpr FunctionRef(F&& f) noexcept : m_obj(const_cast<void*>(reinterpret_cast<const void*>(std::addressof(f)))) , m_callback([](void* obj, Args... args) -> Return { return (*reinterpret_cast<typename std::add_pointer<F>::type>(obj))(std::forward<Args>(args)...); }) { } constexpr void swap(ThisType& rhs) noexcept { std::swap(m_obj, rhs.m_obj); std::swap(m_callback, rhs.m_callback); } Return operator()(Args... args) const { return m_callback(m_obj, std::forward<Args>(args)...); } // Converting FunctionRef to std::function technically can work, but is // almost guaranteed to be a bug template <typename Signature> operator std::function<Signature>() const = delete; template <typename Signature> operator std::function<Signature>() = delete; template <typename Signature> operator UniqueFunction<Signature>() const = delete; template <typename Signature> operator UniqueFunction<Signature>() = delete; // FunctionRef cannot be null constexpr FunctionRef(std::nullptr_t) noexcept = delete; private: void* m_obj; Return (*m_callback)(void*, Args...); }; template <typename R, typename... Args> constexpr void swap(FunctionRef<R(Args...)>& lhs, FunctionRef<R(Args...)>& rhs) noexcept { lhs.swap(rhs); } } // namespace realm::util #ifdef _WIN32 #pragma warning(pop) #endif #endif