Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 110 additions & 7 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -1675,22 +1675,125 @@ unittest
// Aggregate Types
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://

/**
Determines whether $(D T) has its own context pointer.
$(D T) must be either $(D class), $(D struct), or $(D union).
*/
template isNested(T)
if(is(T == class) || is(T == struct) || is(T == union))
{
enum isNested = __traits(isNested, T);
}

/**
Determines whether $(D T) or any of its representation types
have a context pointer.
*/
template hasNested(T)
{
static if(isStaticArray!T && T.length)
enum hasNested = hasNested!(typeof(T.init[0]));
else static if(is(T == class) || is(T == struct) || is(T == union))
enum hasNested = isNested!T ||
anySatisfy!(.hasNested, FieldTypeTuple!T);
else
enum hasNested = false;
}

unittest
{
static assert(!__traits(compiles, isNested!int));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isNested causes compile error for non struct/union types? Rather simply returns false for them is better IMO.
Do you have any strong argument about that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By restricting types isNested can accept we reduce a potential to use it incorrectly as using isNested not inside static if(is(T == struct) ...) is dangerous. I.e. we unsure isNested will be used only if a user has a struct or union he'd like to test and hasNested will be used otherwise.

I hope in the future we will be able to check if a class is nested. Probably you can help with this by adding a new __trait. And isNested will also be able to check classes. For now if it will accept class it will silently work incorrect. And, illustrating my point in the first paragraph, we will have to carefully rewrite hasNested to not make a mistake.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the current compiler's implementation, some hacky code might work for the nested class.
(This is rough code, and does not consider filed alignments).

class C {
    int x;
    class D { int y; }
}
void main() {
    pragma(msg, "end = ", C.tupleof[$-1].offsetof, ", size = ", __traits(classInstanceSize, C));
    pragma(msg, "end = ", C.D.tupleof[$-1].offsetof, ", size = ", __traits(classInstanceSize, C.D));
}

static assert(!hasNested!int);

static struct StaticStruct { }
static assert(!isNested!StaticStruct);
static assert(!hasNested!StaticStruct);

int i;
struct NestedStruct { void f() { ++i; } }
static assert( isNested!NestedStruct);
static assert( hasNested!NestedStruct);
static assert( isNested!(immutable NestedStruct));
static assert( hasNested!(immutable NestedStruct));

static assert(!__traits(compiles, isNested!(NestedStruct[1])));
static assert( hasNested!(NestedStruct[1]));
static assert(!hasNested!(NestedStruct[0]));

struct S1 { NestedStruct nested; }
static assert(!isNested!S1);
static assert( hasNested!S1);

static struct S2 { NestedStruct nested; }
static assert(!isNested!S2);
static assert( hasNested!S2);

static struct S3 { NestedStruct[0] nested; }
static assert(!isNested!S3);
static assert(!hasNested!S3);

static union U { NestedStruct nested; }
static assert(!isNested!U);
static assert( hasNested!U);

static class StaticClass { }
static assert(!isNested!StaticClass);
static assert(!hasNested!StaticClass);

class NestedClass { void f() { ++i; } }
static assert( isNested!NestedClass);
static assert( hasNested!NestedClass);
static assert( isNested!(immutable NestedClass));
static assert( hasNested!(immutable NestedClass));

static assert(!__traits(compiles, isNested!(NestedClass[1])));
static assert( hasNested!(NestedClass[1]));
static assert(!hasNested!(NestedClass[0]));
}


/***
* Get the types of the fields of a struct or class.
* Get as a typetuple the types of the fields of a struct, class, or union.
* This consists of the fields that take up memory space,
* excluding the hidden fields like the virtual function
* table pointer.
* table pointer or a context pointer for nested types.
* If $(D T) isn't a struct, class, or union returns typetuple
* with one element $(D T).
*/

template FieldTypeTuple(S)
template FieldTypeTuple(T)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andralex: Shouldn't we try to constraint this template to only aggregate types? I don't see the use case for FieldTypeTuple!int == TypeTuple!int. Although this template was always like this and might break code if we add the constraint.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, not worth the potential breakage imho.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May help some recursion in client code. Having this closure property is nice I think.

{
static if (is(S == struct) || is(S == class) || is(S == union))
alias typeof(S.tupleof) FieldTypeTuple;
static if (is(T == struct) || is(T == union))
alias typeof(T.tupleof[0 .. $ - isNested!T]) FieldTypeTuple;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay, boolean to integer promotion rules…

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing though: Is it guaranteed the context pointer is always the last field? Is this defined in the spec anywhere?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if it is defined somewhere (I couldn't find anything either when I looked for a spec on the ABI side of this). This is, however, as far as I can see currently the only sane implementation of the trait, and the unit test will blow up if that behavior ever changes.

else static if (is(T == class))
alias typeof(T.tupleof) FieldTypeTuple;
else
alias TypeTuple!S FieldTypeTuple;
//static assert(0, "argument is not struct or class");
alias TypeTuple!T FieldTypeTuple;
}

unittest
{
static assert(is(FieldTypeTuple!int == TypeTuple!int));

static struct StaticStruct1 { }
static assert(is(FieldTypeTuple!StaticStruct1 == TypeTuple!()));

static struct StaticStruct2 { int a, b; }
static assert(is(FieldTypeTuple!StaticStruct2 == TypeTuple!(int, int)));

int i;

struct NestedStruct1 { void f() { ++i; } }
static assert(is(FieldTypeTuple!NestedStruct1 == TypeTuple!()));

struct NestedStruct2 { int a; void f() { ++i; } }
static assert(is(FieldTypeTuple!NestedStruct2 == TypeTuple!int));

class NestedClass { int a; void f() { ++i; } }
static assert(is(FieldTypeTuple!NestedClass == TypeTuple!int));
}


// // FieldOffsetsTuple
// private template FieldOffsetsTupleImpl(size_t n, T...)
// {
Expand Down