/**
 * D header file for interaction with C++ std::array.
 *
 * Copyright: Copyright (c) 2018 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/array.d)
 */

module core.stdcpp.array;

// hacks to support DMD on Win32
version (CppRuntime_Microsoft)
{
    version = CppRuntime_Windows; // use the MS runtime ABI for win32
}
else version (CppRuntime_DigitalMars)
{
    version = CppRuntime_Windows; // use the MS runtime ABI for win32
    pragma(msg, "std::array not supported by DMC");
}
version (CppRuntime_Clang)
{
    private alias AliasSeq(Args...) = Args;
    private enum StdNamespace = AliasSeq!("std", "__1");
}
else
{
    private enum StdNamespace = "std";
}

extern(C++, (StdNamespace)):

/**
 * D language counterpart to C++ std::array.
 *
 * C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/container/array)
 */
extern(C++, class) struct array(T, size_t N)
{
extern(D):
pragma(inline, true):

    ///
    alias size_type = size_t;
    ///
    alias difference_type = ptrdiff_t;
    ///
    alias value_type = T;
    ///
    alias pointer = T*;
    ///
    alias const_pointer = const(T)*;

    ///
    alias as_array this;

    /// Variadic constructor
    this(T[N] args ...)                                 { this[] = args[]; }

    ///
    void fill()(auto ref const(T) value)                { this[] = value; }

pure nothrow @nogc:
    ///
    size_type size() const @safe                        { return N; }
    ///
    alias length = size;
    ///
    alias opDollar = length;
    ///
    size_type max_size() const @safe                    { return N; }
    ///
    bool empty() const @safe                            { return N == 0; }

    ///
    ref inout(T) front() inout @safe                    { static if (N > 0) { return this[0]; } else { return as_array()[][0]; /* HACK: force OOB */ } }
    ///
    ref inout(T) back() inout @safe                     { static if (N > 0) { return this[N-1]; } else { return as_array()[][0]; /* HACK: force OOB */ } }

    version (CppRuntime_Windows)
    {
        ///
        inout(T)* data() inout @safe                    { return &_Elems[0]; }
        ///
        ref inout(T)[N] as_array() inout @safe          { return _Elems[0 .. N]; }
        ///
        ref inout(T) at(size_type i) inout @safe        { return _Elems[0 .. N][i]; }

    private:
        T[N ? N : 1] _Elems;
    }
    else version (CppRuntime_Gcc)
    {
        ///
        inout(T)* data() inout @safe                    { static if (N > 0) { return &_M_elems[0]; } else { return null; } }
        ///
        ref inout(T)[N] as_array() inout @trusted       { return cast(inout(T)[N])data()[0 .. N]; }
        ///
        ref inout(T) at(size_type i) inout @trusted     { return data()[0 .. N][i]; }

    private:
        static if (N > 0)
        {
            T[N] _M_elems;
        }
        else
        {
            struct _Placeholder {}
            _Placeholder _M_placeholder;
        }
    }
    else version (CppRuntime_Clang)
    {
        ///
        inout(T)* data() inout @trusted                 { static if (N > 0) { return &__elems_[0]; } else { return cast(inout(T)*)__elems_.ptr; } }
        ///
        ref inout(T)[N] as_array() inout @trusted       { return cast(inout(T)[N])data()[0 .. N]; }
        ///
        ref inout(T) at(size_type i) inout @trusted     { return data()[0 .. N][i]; }

    private:
        static if (N > 0)
        {
            T[N] __elems_;
        }
        else
        {
            struct _ArrayInStructT { T[1] __data_; }
            align(_ArrayInStructT.alignof)
            byte[_ArrayInStructT.sizeof] __elems_ = void;
        }
    }
    else
    {
        static assert(false, "C++ runtime not supported");
    }
}
