// // Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd"). // Copyright (c) YaPB Development Team. // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: // https://yapb.ru/license // #pragma once #include #include CR_NAMESPACE_BEGIN static constexpr uint32 kLambdaSmallBufferSize = sizeof (void *) * 16; template class Lambda; template class Lambda { class LambdaFunctorWrapper { public: LambdaFunctorWrapper () = default; virtual ~LambdaFunctorWrapper () = default; public: virtual void move (uint8 *to) = 0; virtual void small (uint8 *to) const = 0; virtual R invoke (Args &&...) = 0; virtual UniquePtr clone () const = 0; public: void operator delete (void *ptr) { alloc.deallocate (ptr); } }; template class LambdaFunctor : public LambdaFunctorWrapper { private: T m_callable; public: LambdaFunctor (const T &callable) : LambdaFunctorWrapper (), m_callable (callable) { } LambdaFunctor (T &&callable) : LambdaFunctorWrapper (), m_callable (cr::move (callable)) { } ~LambdaFunctor () override = default; public: void move (uint8 *to) override { new (to) LambdaFunctor (cr::move (m_callable)); } void small (uint8 *to) const override { new (to) LambdaFunctor (m_callable); } R invoke (Args &&... args) override { return m_callable (cr::forward (args)...); } UniquePtr clone () const override { return createUniqueBase , LambdaFunctorWrapper> (m_callable); } }; union { UniquePtr m_functor; uint8 m_small[kLambdaSmallBufferSize]; }; bool m_smallObject; private: void destroy () { if (m_smallObject) { reinterpret_cast (m_small)->~LambdaFunctorWrapper (); } else { m_functor.reset (); } } void swap (Lambda &rhs) noexcept { cr::swap (rhs, *this); } public: Lambda () noexcept : Lambda (nullptr) { } Lambda (decltype (nullptr)) noexcept : m_functor (nullptr), m_smallObject (false) { } Lambda (const Lambda &rhs) { if (rhs.m_smallObject) { reinterpret_cast (rhs.m_small)->small (m_small); } else { new (m_small) UniquePtr (rhs.m_functor->clone ()); } m_smallObject = rhs.m_smallObject; } Lambda (Lambda &&rhs) noexcept { if (rhs.m_smallObject) { reinterpret_cast (rhs.m_small)->move (m_small); new (rhs.m_small) UniquePtr (nullptr); } else { new (m_small) UniquePtr (cr::move (rhs.m_functor)); } m_smallObject = rhs.m_smallObject; rhs.m_smallObject = false; } template Lambda (F function) { if (cr::fix (sizeof (function) > kLambdaSmallBufferSize)) { m_smallObject = false; new (m_small) UniquePtr (createUniqueBase , LambdaFunctorWrapper> (cr::move (function))); } else { m_smallObject = true; new (m_small) LambdaFunctor (cr::move (function)); } } ~Lambda () { destroy (); } public: Lambda &operator = (const Lambda &rhs) { destroy (); Lambda tmp (rhs); swap (tmp); return *this; } Lambda &operator = (Lambda &&rhs) noexcept { destroy (); if (rhs.m_smallObject) { reinterpret_cast (rhs.m_small)->move (m_small); new (rhs.m_small) UniquePtr (nullptr); } else { new (m_small) UniquePtr (cr::move (rhs.m_functor)); } m_smallObject = rhs.m_smallObject; rhs.m_smallObject = false; return *this; } explicit operator bool () const noexcept { return m_smallObject || !!m_functor; } public: R operator () (Args ...args) { return m_smallObject ? reinterpret_cast (m_small)->invoke (cr::forward (args)...) : m_functor->invoke (cr::forward (args)...); } }; CR_NAMESPACE_END