// EXTRA_CPP_SOURCES: cpp_abi_tests.cpp

extern(C++) {

struct S
{
    float a = 1;
}

bool   passthrough(bool   value);
byte   passthrough(byte   value);
ubyte  passthrough(ubyte  value);
char   passthrough(char   value);
dchar  passthrough(dchar  value);
short  passthrough(short  value);
ushort passthrough(ushort value);
int    passthrough(int    value);
uint   passthrough(uint   value);
long   passthrough(long   value);
ulong  passthrough(ulong  value);
float  passthrough(float  value);
double passthrough(double value);
S      passthrough(S      value);

bool   passthrough_ptr(bool   *value);
byte   passthrough_ptr(byte   *value);
ubyte  passthrough_ptr(ubyte  *value);
char   passthrough_ptr(char   *value);
dchar  passthrough_ptr(dchar  *value);
short  passthrough_ptr(short  *value);
ushort passthrough_ptr(ushort *value);
int    passthrough_ptr(int    *value);
uint   passthrough_ptr(uint   *value);
long   passthrough_ptr(long   *value);
ulong  passthrough_ptr(ulong  *value);
float  passthrough_ptr(float  *value);
double passthrough_ptr(double *value);
S      passthrough_ptr(S      *value);

bool   passthrough_ref(ref bool   value);
byte   passthrough_ref(ref byte   value);
ubyte  passthrough_ref(ref ubyte  value);
char   passthrough_ref(ref char   value);
dchar  passthrough_ref(ref dchar  value);
short  passthrough_ref(ref short  value);
ushort passthrough_ref(ref ushort value);
int    passthrough_ref(ref int    value);
uint   passthrough_ref(ref uint   value);
long   passthrough_ref(ref long   value);
ulong  passthrough_ref(ref ulong  value);
float  passthrough_ref(ref float  value);
double passthrough_ref(ref double value);
S      passthrough_ref(ref S      value);
}

template IsSigned(T)
{
    enum IsSigned = is(T==byte)  ||
                    is(T==short) ||
                    is(T==int)   ||
                    is(T==long);
}

template IsUnsigned(T)
{
    enum IsUnsigned = is(T==ubyte)  ||
                      is(T==ushort) ||
                      is(T==uint)   ||
                      is(T==ulong);
}

template IsIntegral(T)
{
    enum IsIntegral = IsSigned!T || IsUnsigned!T;
}

template IsFloatingPoint(T)
{
    enum IsFloatingPoint = is(T==float) || is(T==double) || is(T==real);
}

template IsBoolean(T)
{
    enum IsBoolean = is(T==bool);
}

template IsSomeChar(T)
{
    enum IsSomeChar = is(T==char) || is(T==dchar);
}

void check(T)(T actual, T expected)
{
    assert(actual is expected);
}

void check(T)(T value)
{
    check(passthrough(value), value);
    check(passthrough_ptr(&value), value);
    check(passthrough_ref(value), value);
}

T[] values(T)()
{
    T[] values;
    static if(IsBoolean!T)
    {
        values ~= true;
        values ~= false;
    }
    else static if(IsSomeChar!T)
    {
        values ~= T.init;
        values ~= T('a');
        values ~= T('z');
    }
    else
    {
        values ~= T(0);
        values ~= T(1);
        static if(IsIntegral!T)
        {
            static if(IsSigned!T) values ~= T.min;
            values ~= T.max;
        }
        else static if(IsFloatingPoint!T)
        {
            values ~= T.nan;
            values ~= T.min_normal;
            values ~= T.max;
        }
        else
        {
            assert(0);
        }
    }
    return values;
}

void main()
{
    foreach(bool val; values!bool())     check(val);
    foreach(byte val; values!byte())     check(val);
    foreach(ubyte val; values!ubyte())   check(val);
    foreach(char val; values!char())     check(val);
    foreach(dchar val; values!dchar())   check(val);
    foreach(short val; values!short())   check(val);
    foreach(ushort val; values!ushort()) check(val);
    foreach(int val; values!int())       check(val);
    foreach(uint val; values!uint())     check(val);
    foreach(long val; values!long())     check(val);
    foreach(ulong val; values!ulong())   check(val);
    foreach(float val; values!float())   check(val);
    foreach(double val; values!double()) check(val);
    check(S());
}
