www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Suggestion: class/struct tuples preserve anonymous unions/structs

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
If you write something like this:

struct S
{
    int x;

    union U
    {
        int a;
        int b;
    }

    U u;
}

And print out its field types:

foreach(T; FieldTypeTuple!(S))
    writefln(typeid(T));

It will print out

int
test.S.U

But if you make the union anonymous:

struct S
{
    int x;

    union
    {
        int a;
        int b;
    }
}

The output becomes:

int
int
int

The anonymous union is getting "flattened out" by the type tuple mechanism.

This messes up some automation features.  For example I'm trying to write a 
simple "Serialize" function which can serialize entire structs.  But unions 
can't be automatically serialized, since the Serialize function has no idea 
which member of the union is currently "valid."  So I have error checking to 
disallow serializing unions, but since the .tupleof facility flattens out 
anonymous unions, it can't check for errors (and therefore I don't know that 
there's a problem until I write the struct out to a file and end up with a 
bunch of invalid members, since they came from the anonymous union).

I'm not sure how anonymous unions and structs are handled by the compiler. 
If they are created as "secret" types, would it then be possible for the 
above struct with the anonymous union to consist of:

int
test.S.__UNION0

or something like that? 
Feb 14 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Jarrett Billingsley wrote:
 The anonymous union is getting "flattened out" by the type tuple mechanism.
 
 This messes up some automation features.  For example I'm trying to write a 
 simple "Serialize" function which can serialize entire structs.  But unions 
 can't be automatically serialized, since the Serialize function has no idea 
 which member of the union is currently "valid."  So I have error checking to 
 disallow serializing unions, but since the .tupleof facility flattens out 
 anonymous unions, it can't check for errors (and therefore I don't know that 
 there's a problem until I write the struct out to a file and end up with a 
 bunch of invalid members, since they came from the anonymous union).
 
 I'm not sure how anonymous unions and structs are handled by the compiler. 
 If they are created as "secret" types, would it then be possible for the 
 above struct with the anonymous union to consist of:
 
 int
 test.S.__UNION0
 
 or something like that? 

I don't think it's being "flattened out" by the type tuple mechanism, I think it happens *before* that. They're not created as secret types, they just seem to use different rules for offset calculation. I'm not really sure whether or not this is a bug in the compiler, but it happens. The following template can be used to check for unions (anonymous and named) and generate compile errors when it finds any: ----- /** static asserts when T is a union or contains unions. * Note: the 'Idx' parameter should be left at the default by * code outside the template. */ template validate(T, size_t Idx = 0) { static assert(!is(T == union), T.stringof ~ " is a union itself!"); static assert(!is(typeof(T.tupleof)[Idx] == union), "Member " ~ Idx.stringof ~ " of " ~ T.stringof ~ " is a union"); static if (Idx + 1 < T.tupleof.length) { // check overlap caused by anonymous union members static assert(T.tupleof[Idx].offsetof + T.tupleof[Idx].sizeof <= T.tupleof[Idx + 1].offsetof, "Member " ~ Idx.stringof ~ " of " ~ T.stringof ~ " overlaps the next member"); // and check the other members mixin validate!(T, Idx + 1); } static if (Idx < T.tupleof.length) { // Recurse into member structs static if (is(typeof(T.tupleof)[Idx] == struct)) mixin validate!(typeof(T.tupleof)[Idx]); // uncomment these lines to recurse into member class references as well: //else static if (is(typeof(T.tupleof)[Idx] == class)) // mixin validate!(typeof(T.tupleof)[Idx]); } } // // Some test code: // struct SAnonUnion { int x; union { int a; int b; } } struct SUnion { int x; union U { int a; int b; } U u; } struct SValid { int x; int a; int b; } class Class { SUnion u; } struct SNested { Class u; } void main() { //mixin validate!(SAnonUnion); // error :) //mixin validate!(SUnion); // error :) mixin validate!(SNested); // error if class references are followed, or 'Class' is changed to a struct mixin validate!(SValid); } ----- (You may have to correct some line wrapping before it'll compile)
Feb 15 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Frits van Bommel" <fvbommel REMwOVExCAPSs.nl> wrote in message 
news:er1cc3$3sr$1 digitalmars.com...
 I don't think it's being "flattened out" by the type tuple mechanism, I 
 think it happens *before* that. They're not created as secret types, they 
 just seem to use different rules for offset calculation.
 I'm not really sure whether or not this is a bug in the compiler, but it 
 happens.

I was kind of worried about that. Well we can hope..
 template validate(T, size_t Idx = 0) {
     static assert(!is(T == union), T.stringof ~ " is a union itself!");
     static assert(!is(typeof(T.tupleof)[Idx] == union),
         "Member " ~ Idx.stringof ~ " of " ~ T.stringof ~ " is a union");

     static if (Idx + 1 < T.tupleof.length) {
         // check overlap caused by anonymous union members
         static assert(T.tupleof[Idx].offsetof + T.tupleof[Idx].sizeof <= 
 T.tupleof[Idx + 1].offsetof,
             "Member " ~ Idx.stringof ~ " of " ~ T.stringof ~ " overlaps 
 the next member");
         // and check the other members
         mixin validate!(T, Idx + 1);
     }

     static if (Idx < T.tupleof.length) {
         // Recurse into member structs
         static if (is(typeof(T.tupleof)[Idx] == struct))
             mixin validate!(typeof(T.tupleof)[Idx]);

         // uncomment these lines to recurse into member class references 
 as well:
         //else static if (is(typeof(T.tupleof)[Idx] == class))
         //    mixin validate!(typeof(T.tupleof)[Idx]);
     }
 }

Thanks for this :) I was thinking it might be possible to check the .offsetofs.
Feb 15 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Jarrett Billingsley wrote:
[snip code]
 
 Thanks for this :)  I was thinking it might be possible to check the 
 .offsetofs. 

Something I just thought of: that code doesn't take typedefs into account. That loophole can be closed by using is(T Base == typedef) and inspecting Base recursively if it evaluates to true. Implementing this is left as an exercise to the reader ;).
Feb 15 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Frits van Bommel" <fvbommel REMwOVExCAPSs.nl> wrote in message 
news:er1ost$mae$1 digitalmars.com...
 Jarrett Billingsley wrote:
 [snip code]
 Thanks for this :)  I was thinking it might be possible to check the 
 .offsetofs.

Something I just thought of: that code doesn't take typedefs into account. That loophole can be closed by using is(T Base == typedef) and inspecting Base recursively if it evaluates to true. Implementing this is left as an exercise to the reader ;).

WAITWaitwaitwait. When the *hell* was .stringof added? :D
Feb 15 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:er1s3r$qqa$1 digitalmars.com...

 WAITWaitwaitwait.  When the *hell* was .stringof added?

 :D

I guess I missed the mini-conversation in the DMD 1.005 thread.. man! What a cool feature :)
Feb 15 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Jarrett Billingsley wrote:
 "Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
 news:er1s3r$qqa$1 digitalmars.com...
 
 WAITWaitwaitwait.  When the *hell* was .stringof added?

 :D

I guess I missed the mini-conversation in the DMD 1.005 thread.. man! What a cool feature :)

Yes it is :). It's not really essential here though, I just used it to provide more readable error messages for assertion failures...
Feb 15 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Jarrett Billingsley wrote:
 I'm not sure how anonymous unions and structs are handled by the compiler. 

This has nothing to do with tuples. Anonymous structs and unions are used for layout only, the members are 'promoted' into the enclosing aggregate.
Feb 15 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Walter Bright wrote:
 Jarrett Billingsley wrote:
 I'm not sure how anonymous unions and structs are handled by the 
 compiler. 

This has nothing to do with tuples. Anonymous structs and unions are used for layout only, the members are 'promoted' into the enclosing aggregate.

It's fairly obvious this is what's happening. And normally this wouldn't matter as you wouldn't be able see the difference, but because of .tupleof this is now detectable. I think the real question here is: while this may be what *is* happening, is this also what *should* happen? (i.e. "Is this a bug?") I can't find anything specifying this behavior in the spec...
Feb 15 2007
parent reply Sean Kelly <sean f4.ca> writes:
Frits van Bommel wrote:
 Walter Bright wrote:
 Jarrett Billingsley wrote:
 I'm not sure how anonymous unions and structs are handled by the 
 compiler. 

This has nothing to do with tuples. Anonymous structs and unions are used for layout only, the members are 'promoted' into the enclosing aggregate.

It's fairly obvious this is what's happening. And normally this wouldn't matter as you wouldn't be able see the difference, but because of ..tupleof this is now detectable. I think the real question here is: while this may be what *is* happening, is this also what *should* happen? (i.e. "Is this a bug?") I can't find anything specifying this behavior in the spec...

I think as long as .offsetof can be obtained for the values then everything should be fine. You know members are part of a union when their .offsetof is the same. Sean
Feb 15 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 Frits van Bommel wrote:
 Walter Bright wrote:
 Jarrett Billingsley wrote:
 I'm not sure how anonymous unions and structs are handled by the 
 compiler. 

This has nothing to do with tuples. Anonymous structs and unions are used for layout only, the members are 'promoted' into the enclosing aggregate.

It's fairly obvious this is what's happening. And normally this wouldn't matter as you wouldn't be able see the difference, but because of ..tupleof this is now detectable. I think the real question here is: while this may be what *is* happening, is this also what *should* happen? (i.e. "Is this a bug?") I can't find anything specifying this behavior in the spec...

I think as long as .offsetof can be obtained for the values then everything should be fine. You know members are part of a union when their .offsetof is the same.

Yeah, you can figure it out like this, but I do think exposing anonymous structs and unions as actual struct an union members would lead to cleaner code for some cases. Even if this only happens when it's nested in an aggregate of the other type (or a union in a class).
Feb 15 2007