🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

5 total messages Started by Dirk Becker Sat, 03 Feb 1996 17:22
class.union questions
#3225
Author: Dirk Becker
Date: Sat, 03 Feb 1996 17:22
135 lines
5581 bytes
Hello, C++ gurus!

In Section 9.6.1 Unions [class.union] there is a large collection of
restrictions on the union itself and on candidate member classes.
I would like to ask for reasons of several of them:

a)  "A union shall not have base classes. A union shall not be used as
     a base class."

This prevents any construction like the following, where one would
like to extend the union contained in some base class:

class GenericInterpreter {
    union opcode {
        short tag;
        struct { short tag; short data; } op1;
        struct { short tag; char* data; } op2;
    };
};

class SpecialInterpreter : public GenericInterpreter {
    union opcode : public GenericInterpreter::opcode {
        struct { short tag; long  data; } op3;
        struct { short tag; float data; } op4;
    };
};

b) "An object of a class with a non-trivial default constructor
    (_class.ctor_), a non-trivial copy constructor (_class.copy_),
    a non-trivial destructor (_class.dtor_), or a non-trivial copy
    assignment operator (_over.ass_, _class.copy_) cannot be a member
    of a union,"

This is a major reduction of the restrictions found in ARM, where all
kinds of constructors or assignment operators were excluded. Here is
also a note to give the explanation by the assumption, any member
functions and especially assignments would usually expect a correctly
constructed object.

The quoted WP sequence tries its best to exclude virtual function
tables from the union members, which is just fine. But through the
non-trivialness you also exclude _any_ custom make of the copy
constructor and assignment operator.
So you can have any special assignment operator or constructor for
your member objects, any but the most common and useful ones.

Another sample:

class longlong { ... };
struct opcode {
    short tag;
    union {
        short     op1;
        long      op2;
        longlong  op3;
    } data;
};

If you are lucky to have some native long long datatype, then this
would be legal code. If you already had to implement your own
class longlong, now you lost the chance to use it (here).

c) "A union can be thought of as a class"

Here you accept the implications of 12.8.8 and 12.8.13 on implicitly
defined copy constructor and copy assignment. The already trivial
copy constructor/assignment of the member objects will then result
in one large repetition of copies from and to the same memory locations.
To avoid this behaviour I would strongly recommend to implement your
own copy constructor or assignment whenever you use a union, because a
memberwise copy is usually not desired.

Let's combine this with the problems of a) and b):

class GenericInterpreter {
    union opcode {
        opcode(const opcode&);               // replace implicitly defined
        opcode& operator = (const opcode&);  // memberwise copy versions

        short tag;
        struct { short tag; short data; } op1;
        struct { short tag; char* data; } op2;
    };
};

class SpecialInterpreter : public GenericInterpreter {
    union opcode {
        GenericInterpreter::opcode      generic; // error: non-trivial member
        short tag;
        struct { short tag; long  data; } op3;
        struct { short tag; float data; } op4;
    };
};

class ValidInterpreter : public GenericInterpreter {
    union opcode {
        // using copy - paste instead of language features
        short tag;
        struct { short tag; short data; } op1;
        struct { short tag; char* data; } op2;
        //
        struct { short tag; long  data; } op3;
        struct { short tag; float data; } op4;
    };
};

In my opinion, comparing the copy constructor/assignments to others, they
are only special regarding their use by the implicitly defined memberwise
copy constructor/assignment by outside classes. Here I can't find a reason
why a member's custom copy methods should not be applicable instead.

Conclusion, just dreaming:
 I would generally prefer " ... constructors and destructor of member
 objects are ignored, if not explicitly called by the union's ctor/dtor.
 The union's implicit default copy constructor / assignment does a
 binary copy instead of memberwise copy. Objects <containing a vptr>
 cannot be member of a union" - Please pardon my shortcut on vptrs.

d) anonymous union

Why is there no anonymous struct? This is not only for symmetric reasons,
but anonymous structs inside unions may be as useful as anonymous unions
inside structs.


Best regards
Dirk

------------------------------------------------------------------
Dirk Becker                                dirk@becker.adviser.com
Harderweg 76, 22549 Hamburg, Germany          Tel. +49 40 80783143
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: class.union questions
#3236
Author: John Max Skaller
Date: Mon, 05 Feb 1996 17:27
117 lines
5364 bytes
Dirk Becker wrote:
> 
> Hello, C++ gurus!
> 
> In Section 9.6.1 Unions [class.union] there is a large collection of
> restrictions on the union itself and on candidate member classes.
> I would like to ask for reasons of several of them:

	I agree, I wrote a proposal to fix this. And, three countries
voted AGAINST adding yet another restriction to the list (references)
causing the motion to be withdrawn -- and then reintroduced
at a later meeting when two of the NB's were unable to send a delegate
for financial reasons.

> a)  "A union shall not have base classes. A union shall not be used as
>      a base class."

	This is in clear contravention of the clause that
says a union is a class -- and says that classes can have bases.
IMHO.
 
> This prevents any construction like the following, where one would
> like to extend the union contained in some base class:
> 
> class GenericInterpreter {
>     union opcode {
>         short tag;
>         struct { short tag; short data; } op1;
>         struct { short tag; char* data; } op2;
>     };
> };
> 
> class SpecialInterpreter : public GenericInterpreter {
>     union opcode : public GenericInterpreter::opcode {
>         struct { short tag; long  data; } op3;
>         struct { short tag; float data; } op4;
>     };
> };

   This is a very interesting idea. I'd not thought about that.
 
> b) "An object of a class with a non-trivial default constructor
>     (_class.ctor_), a non-trivial copy constructor (_class.copy_),
>     a non-trivial destructor (_class.dtor_), or a non-trivial copy
>     assignment operator (_over.ass_, _class.copy_) cannot be a member
>     of a union,"
> 
> This is a major reduction of the restrictions found in ARM, where all
> kinds of constructors or assignment operators were excluded. 

	No. It is merely a more precise statement of the same
restrictions using more technical terminology.

> c) "A union can be thought of as a class"

	Beats me how this can be, in view of the above constraints.
 
> In my opinion, comparing the copy constructor/assignments to others, they
> are only special regarding their use by the implicitly defined memberwise
> copy constructor/assignment by outside classes. Here I can't find a reason
> why a member's custom copy methods should not be applicable instead.

       	Absolutely. I agree. The current restrictions are valid,
but the prohibition is in the WRONG PLACE.

	You SHOULD be allowed to put constructible objects
in a union. All this does is prevent the compiler generating
an assignment operator. So if you try to use it, you get an error,
and have to define your own.

> d) anonymous union
> 
> Why is there no anonymous struct? 

	The committee (as a body) does not find arguments for 
orthogonal design appealing. It is hard to demonstrate
why orthogonal design is good to those who have little
experience with orthogonal systems -- most of us,
considering the horrid systems we're forced to work with
most of the time.

	So, where Bjarne was able to provide good design
we are lucky, and where he did not have time, or did not
personally consider it important, or just plain
messed up, we do not -- with one exception: STL
(which is designed better than most parts of C++ itself).

	On a more interesting note: templates are
NOT powerful enough to fix this problem (brain dead
unions) BUT they are ALMOST enough. In conjunction
with a header file generator program, an emulation
of discriminated unions is available (mail me).
Also available the logical generalisation of
STL "pair" to order n.

	[I'm attempting to upgrade these facilities
so they work with FUNCTIONS as well as data structures.
There are some serious obstacles inherent in the
non-orthogonal core language design, preventing
higher order genericity from working. What I have
working currently requires a generator which produces
O(n^n) declarations. For example a struct of order
10 with associated generic operators is useable,
the header is about 1Meg. Much above that -- needed
in practical applications -- requires compiler
support to "generate when required"]


-- 
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 web: http://www.maxtal.com.au/~skaller/
AUSTRALIA                      email: skaller@maxtal.com.au
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Re: class.union questions
#3252
Author: James Kanze US/ESC 60/3/141 #407...
Date: Tue, 06 Feb 1996 02:35
200 lines
9376 bytes
In article <v01530500ad38ee830c96@[194.163.74.11]>
dirk@becker.adviser.com (Dirk Becker) writes:

|> In Section 9.6.1 Unions [class.union] there is a large collection of
|> restrictions on the union itself and on candidate member classes.
|> I would like to ask for reasons of several of them:

One of the principle goals was that a union be compatible with a C
union, at least in use.  One of the rules is that the addresses of all
of the elements in the union compare equal to the address of the union
itself, when cast to the appropriate type.

|> a)  "A union shall not have base classes. A union shall not be used as
|>      a base class."

Without this rule, you either break the address rule, or you impose
some special class layout on unions.

|> This prevents any construction like the following, where one would
|> like to extend the union contained in some base class:

|> class GenericInterpreter {
|>     union opcode {
|>         short tag;
|>         struct { short tag; short data; } op1;
|>         struct { short tag; char* data; } op2;
|>     };
|> };

|> class SpecialInterpreter : public GenericInterpreter {
|>     union opcode : public GenericInterpreter::opcode {
|>         struct { short tag; long  data; } op3;
|>         struct { short tag; float data; } op4;
|>     };
|> };

This is generally handled better by an abstract base class and virtual
functions.

|> b) "An object of a class with a non-trivial default constructor
|>     (_class.ctor_), a non-trivial copy constructor (_class.copy_),
|>     a non-trivial destructor (_class.dtor_), or a non-trivial copy
|>     assignment operator (_over.ass_, _class.copy_) cannot be a member
|>     of a union,"

|> This is a major reduction of the restrictions found in ARM, where all
|> kinds of constructors or assignment operators were excluded. Here is
|> also a note to give the explanation by the assumption, any member
|> functions and especially assignments would usually expect a correctly
|> constructed object.

This is, in fact, exactly the restriction in the ARM.  All that has
changes is the way things are described.  In the ARM, classes may or
may not have constructors (destructors, assignment operators).  In the
current standard, *EVERY* class/struct has a copy constructor, an
assignment operator and a destructor; the distinction is that some of
these are trivial.

A user declared constructor, destructor or assignment operator is by
definition never trivial.

|> The quoted WP sequence tries its best to exclude virtual function
|> tables from the union members, which is just fine. But through the
|> non-trivialness you also exclude _any_ custom make of the copy
|> constructor and assignment operator.
|> So you can have any special assignment operator or constructor for
|> your member objects, any but the most common and useful ones.

Correct.  The reason is simple: assignment, copy, default construction
and destruction of a union must be correctly defined without the
compiler knowing which element the union actually contains.  The set
of forbidden operators are those that the compiler might be called
upon to use in implicitly generated code.

|> Another sample:

|> class longlong { ... };
|> struct opcode {
|>     short tag;
|>     union {
|>         short     op1;
|>         long      op2;
|>         longlong  op3;
|>     } data;
|> };

|> If you are lucky to have some native long long datatype, then this
|> would be legal code. If you already had to implement your own
|> class longlong, now you lost the chance to use it (here).

This is a problem, but... how would the compiler know when to call the
constructor/destructor of your longlong?

|> c) "A union can be thought of as a class"

|> Here you accept the implications of 12.8.8 and 12.8.13 on implicitly
|> defined copy constructor and copy assignment. The already trivial
|> copy constructor/assignment of the member objects will then result
|> in one large repetition of copies from and to the same memory locations.
|> To avoid this behaviour I would strongly recommend to implement your
|> own copy constructor or assignment whenever you use a union, because a
|> memberwise copy is usually not desired.

Basically, if you study the restrictions on unions carefully, the
rules are such that a bitwise copy *must* be legal for assignment and
copy construction.  This is the only type of copy the compiler can
cope with in the absense of a discriminator to tell it which element
of the union is valid.

IMHO: if you feel that your union needs a copy constructor or an
assignment operator, you are probably doing something wrong, in the
sense that you are trying to use unions in a way that they are not
intended.  (As they are defined in the standard.  If they were defined
differently, they might have different reasonable uses.)

In practice, I don't think I've used a union in my code since
switching to C++, except in memory management code (ensuring
alignment, etc.).

|> Let's combine this with the problems of a) and b):

|> class GenericInterpreter {
|>     union opcode {
|>         opcode(const opcode&);               // replace implicitly defined
|>         opcode& operator = (const opcode&);  // memberwise copy versions

|>         short tag;
|>         struct { short tag; short data; } op1;
|>         struct { short tag; char* data; } op2;
|>     };
|> };

|> class SpecialInterpreter : public GenericInterpreter {
|>     union opcode {
|>         GenericInterpreter::opcode generic; // error: non-trivial member
|>         short tag;
|>         struct { short tag; long  data; } op3;
|>         struct { short tag; float data; } op4;
|>     };
|> };

|> class ValidInterpreter : public GenericInterpreter {
|>     union opcode {
|>         // using copy - paste instead of language features
|>         short tag;
|>         struct { short tag; short data; } op1;
|>         struct { short tag; char* data; } op2;
|>         //
|>         struct { short tag; long  data; } op3;
|>         struct { short tag; float data; } op4;
|>     };
|> };

|> In my opinion, comparing the copy constructor/assignments to others, they
|> are only special regarding their use by the implicitly defined memberwise
|> copy constructor/assignment by outside classes. Here I can't find a reason
|> why a member's custom copy methods should not be applicable instead.

|> Conclusion, just dreaming:
|>  I would generally prefer " ... constructors and destructor of member
|>  objects are ignored, if not explicitly called by the union's ctor/dtor.
|>  The union's implicit default copy constructor / assignment does a
|>  binary copy instead of memberwise copy. Objects <containing a vptr>
|>  cannot be member of a union" - Please pardon my shortcut on vptrs.

There was some discussion concerning this.  There were two points of
view: one supported basically what you are proposing, the other the
current situation.  The current situation won.

I argued the current situation on the grounds that the above is just
too dangerous.  Basically, it makes it too simple to access
unconstructed objects of types which require a constructor.  Most of
the uses I see for a union are at the low level, where C like code is
perfectly appropriate.

IMHO, things like your example are much better handled with derived
classes (in which the vptr replaces the tag for type information).
Even in cases where the union might be more appropriate, what would be
needed is a discriminated union, which is a completely different beast
than the union we now have.  (I've yet to see what I would consider a
convincing example of the advantage of a discriminated union over
polymorphism, but I know that others disagree with me here.)

|> d) anonymous union

|> Why is there no anonymous struct? This is not only for symmetric reasons,
|> but anonymous structs inside unions may be as useful as anonymous unions
|> inside structs.

Agreed here.  Although I really don't see it as worth the bother.  (I
could do without anonymous unions, too.)
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
                -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: class.union questions
#3268
Author: Fergus Henderson
Date: Tue, 06 Feb 1996 18:27
35 lines
2289 bytes
kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:

>dirk@becker.adviser.com (Dirk Becker) writes:
>
>|> In Section 9.6.1 Unions [class.union] there is a large collection of
>|> restrictions on the union itself and on candidate member classes.
>|> I would like to ask for reasons of several of them:
>
>One of the principle goals was that a union be compatible with a C
>union, at least in use.  One of the rules is that the addresses of all
>of the elements in the union compare equal to the address of the union
>itself, when cast to the appropriate type.
>
>|> a)  "A union shall not have base classes. A union shall not be used as
>|>      a base class."
>
>Without this rule, you either break the address rule, or you impose
>some special class layout on unions.

Nevertheless, contrary to what you seem to be implying,
that rule is not necessary for C compatibility.

C++ structs can be very different to C structs, but C compatibility
is preserved for a certain category of C++ structs, namely POD-structs.
In the case of unions, the same thing could have been done -
rule (a) could have been dropped, yet C compatibility could still
have been be supported for POD-unions.

--
Fergus Henderson             	WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au              	PGP: finger fjh@128.250.37.3
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Re: class.union questions
#3334
Author: J. Kanze
Date: Fri, 09 Feb 1996 15:41
56 lines
3564 bytes
In article <4f7l8h$t0e@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus
Henderson) writes:

> kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:
> 
> >dirk@becker.adviser.com (Dirk Becker) writes:
> >
> >|> In Section 9.6.1 Unions [class.union] there is a large collection of
> >|> restrictions on the union itself and on candidate member classes.
> >|> I would like to ask for reasons of several of them:
> >
> >One of the principle goals was that a union be compatible with a C
> >union, at least in use.  One of the rules is that the addresses of all
> >of the elements in the union compare equal to the address of the union
> >itself, when cast to the appropriate type.
> >
> >|> a)  "A union shall not have base classes. A union shall not be used as
> >|>      a base class."
> >
> >Without this rule, you either break the address rule, or you impose
> >some special class layout on unions.
> 
> Nevertheless, contrary to what you seem to be implying,
> that rule is not necessary for C compatibility.
> 
> C++ structs can be very different to C structs, but C compatibility
> is preserved for a certain category of C++ structs, namely POD-structs.
> In the case of unions, the same thing could have been done -
> rule (a) could have been dropped, yet C compatibility could still
> have been be supported for POD-unions.

True.  But there is one intuitive fact that remains true for all
structs: different (data) members have distinct addresses, and in fact
occupy non-overlapping memory.  The same intuitive fact for unions is
that all (non-static) data members have the same address.  (IMHO, of
course.)

There is one restriction that does seem to me totally ungrounded: not
being able to inherit from a union.  I don't think that this feature
would be enormously useful, but I cannot think of a good reason why it
should be banned.  One of the classic solutions for handling C/C++
compatibility is to put all of the data in a PODS, and inherit from it,
using the derived class in C++, but allowing a pointer to it to
automatically become a pointer to the PODS when passed to a C function
declared to take such an address.  The same argument could certainly be
used for inheriting from unions.  If I look in /usr/include/sys, I find
lots of unions which could be potentially used this way.
-- 
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
              -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  Moderation policy:
  http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads