Jump to page: 1 2
Thread overview
Suggestion: class/struct tuples preserve anonymous unions/structs
Feb 15, 2007
Frits van Bommel
Feb 15, 2007
Frits van Bommel
Feb 15, 2007
Frits van Bommel
Feb 15, 2007
Walter Bright
Feb 15, 2007
Frits van Bommel
Feb 15, 2007
Sean Kelly
Feb 15, 2007
Frits van Bommel
February 15, 2007
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?


February 15, 2007
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)
February 15, 2007
"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.


February 15, 2007
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 ;).
February 15, 2007
"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


February 15, 2007
"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 :)


February 15, 2007
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...
February 15, 2007
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.
February 15, 2007
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...
February 15, 2007
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
« First   ‹ Prev
1 2