/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2015-2021 Danny Robson <danny@nerdcruft.net>
 */

#pragma once

#include <functional>
#include <memory>

namespace cruft::memory {
    ///////////////////////////////////////////////////////////////////////////
    /// A deleter object that is deliberately left undefined.
    ///
    /// This allows it to be used in places where the definition of `ValueT`
    /// would otherwise be required (eg, std::unique_ptr). This requires that
    /// the body be explicitly defined by the user; see `DEFERRED_DELETE`.
    template <typename ValueT>
    struct deferred_delete {
        void operator() (ValueT*) const;
    };


    ///------------------------------------------------------------------------
    /// An implementation of deferred delete that just calls `delete` directly.
    #define DEFERRED_DELETE(KLASS)  \
    template <>                     \
    void                            \
    ::cruft::memory::deferred_delete<KLASS>::operator() (KLASS *ptr) const \
    { delete ptr; }


    ///////////////////////////////////////////////////////////////////////////
    /// A convenience typedef for a unique_ptr that uses a deferred_delete
    /// deleter object.
    template <typename ValueT>
    using deferred_unique = std::unique_ptr<ValueT, deferred_delete<ValueT>>;


    ///------------------------------------------------------------------------
    /// A convenience function for creation of deferred_unique objects
    /// analogous to std::make_unique
    template <typename ValueT, typename ...ArgsT>
    deferred_unique<ValueT>
    make_deferred_unique (ArgsT &&...args)
    {
        return deferred_unique<ValueT> (
            new ValueT (
                std::forward<ArgsT> (args)...
            )
        );
    }


    ///////////////////////////////////////////////////////////////////////////
    template <typename T>
    class func_deleter {
    public:
        using func_t = std::function<void(T*)>;

        explicit func_deleter (func_t _func):
            m_func (_func)
        { ; }

        inline void operator() (T *t)
        { m_func (t); }

    private:
        func_t m_func;
    };


    // dispatch object deletion to a known member function.
    //
    // XXX: Generates a "sorry, unimplemented" under GCC (which is
    // effectively an ICE). Their bug tracker seems to indicate they don't
    // give a fuck, so we can't use this except under clang.
    template <
        typename ValueT,
        typename OwnerT,
        void (OwnerT::*Func)(ValueT*)
    >
    class owner_deleter {
    public:
        owner_deleter (OwnerT &owner):
            m_owner (owner)
        { ; }

        inline void operator() (ValueT *t)
        {
            (m_owner.*Func) (t);
        }

    private:
        OwnerT& m_owner;
    };
}