MoveOnlyFunctionImpl.h#

Fully qualified name: omni/detail/MoveOnlyFunctionImpl.h

File members: omni/detail/MoveOnlyFunctionImpl.h

// Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

// Purposely missing #pragma once so it can be included multiple times
#if defined DOXYGEN_BUILD
#    include "../../carb/Defines.h"
#endif

#if !OMNI_MOF && !defined __INTELLISENSE__
#    error Should only be included from MoveOnlyFunction.h
#endif

#include "MoveOnlyFunctionStorage.h"

#ifndef OMNI_MOF_CV
#    define OMNI_MOF_CV
#endif
#ifndef OMNI_MOF_REF
#    define OMNI_MOF_REF
#endif
#ifndef OMNI_MOF_MOVE
#    define OMNI_MOF_MOVE
#endif
#ifndef OMNI_MOF_NOEX
#    define OMNI_MOF_NOEX
#endif

namespace omni
{

template <typename T>
class move_only_function;

template <typename Ret, typename... Args>
class move_only_function<Ret(Args...) OMNI_MOF_CV OMNI_MOF_REF OMNI_MOF_NOEX>
{
public:
    move_only_function() = default;

    move_only_function(std::nullptr_t) noexcept {};

    CARB_PREVENT_COPY(move_only_function);

    template <typename F CARB_NO_DOC(, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, move_only_function>>)>
    move_only_function(F&& f)
    {
        using Ft = std::decay_t<F>;
        if constexpr (std::is_function_v<std::remove_pointer_t<Ft>> || std::is_member_pointer_v<Ft>)
        {
            if (f == nullptr)
            {
                return;
            }
        }
        m_buf.store<Ft>(std::forward<F>(f));
        m_invoker = &_invoke<OMNI_MOF_CV Ft>;
    }

    move_only_function(move_only_function&& other) noexcept
    {
        *this = std::move(other);
    }

    template <typename F CARB_NO_DOC(, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, move_only_function>>)>
    move_only_function& operator=(F&& f)
    {
        using Ft = std::decay_t<F>;
        if constexpr (std::is_function_v<std::remove_pointer_t<Ft>> || std::is_member_pointer_v<Ft>)
        {
            if (f == nullptr)
            {
                *this = nullptr;
                return *this;
            }
        }
        m_buf.store<Ft>(std::forward<F>(f));
        m_invoker = &_invoke<OMNI_MOF_CV Ft>;
        return *this;
    }

    template <typename F, typename... Args_>
    explicit move_only_function(std::in_place_type_t<F>, Args_... args)
    {
        static_assert(std::is_same_v<std::decay_t<F>, F>);
        m_buf.store<F>(std::forward<Args_>(args)...);
        m_invoker = &_invoke<OMNI_MOF_CV F>;
    }

    move_only_function& operator=(move_only_function&& other) noexcept
    {
        if (this != &other)
        {
            m_buf = std::move(other.m_buf);
            m_invoker = std::exchange(other.m_invoker, nullptr);
        }
        return *this;
    }

    move_only_function& operator=(std::nullptr_t) noexcept
    {
        m_buf = nullptr;
        m_invoker = nullptr;
        return *this;
    }

    ~move_only_function() = default;

    Ret operator()(Args... args) OMNI_MOF_CV OMNI_MOF_REF OMNI_MOF_NOEX
    {
        return m_invoker(this, std::forward<Args>(args)...);
    }

    friend bool operator==(const move_only_function& other, std::nullptr_t) noexcept
    {
        return other.m_invoker == nullptr;
    }

    friend bool operator!=(const move_only_function& other, std::nullptr_t) noexcept
    {
        return other.m_invoker != nullptr;
    }

    explicit operator bool() const noexcept
    {
        return m_invoker != nullptr;
    }

private:
    template <typename Ft>
    static Ret _invoke(move_only_function OMNI_MOF_CV* self, Args... args) OMNI_MOF_NOEX
    {
        return std::invoke(OMNI_MOF_MOVE(*self->m_buf.template get<Ft>()), std::forward<Args>(args)...);
    }

    detail::storage<16> m_buf;

    Ret (*m_invoker)(move_only_function OMNI_MOF_CV*, Args...) OMNI_MOF_NOEX = nullptr;
};

#undef OMNI_MOF_CV
#undef OMNI_MOF_REF
#undef OMNI_MOF_MOVE
#undef OMNI_MOF_NOEX

} // namespace omni