/**
* D binding to C++ <memory>.
*
* Copyright: Copyright (c) 2019 D Language Foundation
* License: Distributed under the
*      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
*    (See accompanying file LICENSE)
* Authors:   Manu Evans
* Source:    $(DRUNTIMESRC core/stdcpp/memory.d)
*/

module core.stdcpp.memory;

// LDC: empty module for unsupported C++ runtimes
version (CppRuntime_Microsoft) version = Supported;
else version (CppRuntime_GNU)  version = Supported;
else version (CppRuntime_LLVM) version = Supported;
version (Supported):

public import core.stdcpp.allocator;

import core.stdcpp.xutility : StdNamespace;

extern(C++, (StdNamespace)):

///
unique_ptr!T make_unique(T, Args...)(auto ref Args args)
{
    import core.lifetime : forward;
    import core.stdcpp.new_ : cpp_new;

    return unique_ptr!T(cpp_new!T(forward!args));
}

///
struct default_delete(T)
{
    ///
    alias pointer = ClassOrPtr!T;

    ///
    void opCall()(pointer ptr) const
    {
        import core.stdcpp.new_ : cpp_delete;

        cpp_delete(ptr);
    }
}

///
extern(C++, class)
struct unique_ptr(T, Deleter = default_delete!T)
{
extern(D):
    ///
    this(this) @disable;

    ///
    ~this()
    {
        reset();
    }

    ///
    ref unique_ptr opAssign(typeof(null))
    {
        reset();
        return this;
    }

    ///
    void reset(pointer p = null)
    {
        pointer t = __ptr();
        __ptr() = p;
        if (t)
            get_deleter()(t);
    }

nothrow pure @safe @nogc:
    ///
    alias pointer = ClassOrPtr!T;
    ///
    alias element_type = T;
    ///
    alias deleter_type = Deleter;

    ///
    this(pointer ptr)
    {
        __ptr() = ptr;
    }

    ///
    inout(pointer) get() inout nothrow
    {
        return __ptr();
    }

    ///
    bool opCast(T : bool)() const nothrow
    {
        return __ptr() != null;
    }

    ///
    pointer release() nothrow
    {
        pointer t = __ptr();
        __ptr() = null;
        return t;
    }

//    void swap(ref unique_ptr u) nothrow
//    {
//        __ptr_.swap(__u.__ptr_);
//    }

    version (CppRuntime_Microsoft)
    {
        ///
        ref inout(deleter_type) get_deleter() inout nothrow { return _Mypair._Myval1; }

    private:
        import core.stdcpp.xutility : _Compressed_pair;

        ref pointer __ptr() nothrow { return _Mypair._Myval2; }
        inout(pointer) __ptr() inout nothrow { return _Mypair._Myval2; }

        _Compressed_pair!(Deleter, pointer) _Mypair;
    }
    else version (CppRuntime_GNU)
    {
        ///
        ref inout(deleter_type) get_deleter() inout nothrow { return _M_t.get!1; }

    private:
        import core.stdcpp.tuple : tuple, get;

        ref pointer __ptr() nothrow { return _M_t.get!0; }
        inout(pointer) __ptr() inout nothrow { return _M_t.get!0; }

        tuple!(pointer, Deleter) _M_t;
    }
    else version (CppRuntime_LLVM)
    {
        ///
        ref inout(deleter_type) get_deleter() inout nothrow { return __ptr_.second; }

    private:
        import core.stdcpp.xutility : __compressed_pair;

        ref pointer __ptr() nothrow { return __ptr_.first; }
        inout(pointer) __ptr() inout nothrow { return __ptr_.first; }

        __compressed_pair!(pointer, deleter_type) __ptr_;
    }
}


private:

template ClassOrPtr(T)
{
    static if (is(T == class))
        alias ClassOrPtr = T;
    else
        alias ClassOrPtr = T*;
}
