Topic: Members with operator= in unions?


Author: vals@iol.ie (Valentin Sliouniaev)
Date: 1995/07/26
Raw View
maxtal@Physics.usyd.edu.au (John Max Skaller) wrote:

>In article <KANZE.95Jul18194042@slsvhdt.lts.sel.alcatel.de>,
>James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de> wrote:
>>In article <3u1gjg$51g@barnacle.iol.ie> vals@iol.ie (Valentin
>>Sliouniaev) writes:
>>
>>|> Why is it impossible for a union to have a member with assignment
>>|> operator (as it is said in ARM)?
>>
>>Because the compiler would have no way of knowing whether to call it
>>or not when assigning the union.  Basically, although I don't think
>>that it is explicitly stated, it is implicit that union assignment
>>uses the equivalent of memcpy; this would break if one of the union
>>members had a user provided assignment operator.

> And the correct solution IMHO is for the compiler
>to report an error IF and ONLY IF the user failed to supply
>an assignment operator -- because one was required and could not
>be generated.

> The ARM/WP/CD solution is an overconstraint.
>It places the restriction in the wrong place and is out of
>step with the rest of the language. If the union never got assigned,
>why should there be an error?
>

I think that situation there is the same as with the constructors:

Because all the objects are in the same place in physical memory, it
doesn't make sense to initiallize them at all. Having constructor for
a member (not the whole union) guarantees the member's correct state
after the object (the union) is created. A union as opposed to a
class, cannot guarantee correct state of all its members.

The same is true for copy operator. It guarantees that internals of
the destination (member of a union) and the source are correctly
changed. Copying a class guarantees proper calls of copy operators of
class members. It is an optimization, that, if no member has defined
copy operator, it is possible to copy the whole memory image in one
step. Having copy operators for union members does not guarantee
correct copying of members, if the *union* is copied. May be, copying
of such a union should be disallowed, not creation of it.

This is all correct. What I was talking about was rather a *conversion
operator* (sorry, I did not say that). Please look at an example:

 class rational {
  ...
 public:
  // no constructor - leave garbage in place
  // no destructor - do not close files, etc. :-)
  // no copy operator - do bitwise copy

  rational& operator=(double);
  operator double();
  ....
 };

This is just a definition of another numeric type. Why should this
type be different from int, long, etc.? All I want is to specify a
conversion rules. And oops, I cannot put that into a union...

Is it possible to make it so that "conversion-into" operators will not
be the same as copy operator, and there will be no error in bitwise
copying of such objects?

OR may be there is another way to define a class like rational,
without operator=(), but conversion from double defined?

--VS.







Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: 1995/07/25
Raw View
In article <KANZE.95Jul18194042@slsvhdt.lts.sel.alcatel.de>,
James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de> wrote:
>In article <3u1gjg$51g@barnacle.iol.ie> vals@iol.ie (Valentin
>Sliouniaev) writes:
>
>|> Why is it impossible for a union to have a member with assignment
>|> operator (as it is said in ARM)?
>
>Because the compiler would have no way of knowing whether to call it
>or not when assigning the union.  Basically, although I don't think
>that it is explicitly stated, it is implicit that union assignment
>uses the equivalent of memcpy; this would break if one of the union
>members had a user provided assignment operator.

 And the correct solution IMHO is for the compiler
to report an error IF and ONLY IF the user failed to supply
an assignment operator -- because one was required and could not
be generated.

 The ARM/WP/CD solution is an overconstraint.
It places the restriction in the wrong place and is out of
step with the rest of the language. If the union never got assigned,
why should there be an error?




--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189





Author: osinski@valis.cs.nyu.edu (Ed Osinski)
Date: 1995/07/21
Raw View
In article <KANZE.95Jul18194042@slsvhdt.lts.sel.alcatel.de>, kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:
|> In article <3u1gjg$51g@barnacle.iol.ie> vals@iol.ie (Valentin
|> Sliouniaev) writes:
|>
|> |> Why is it impossible for a union to have a member with assignment
|> |> operator (as it is said in ARM)?
|>
|> Because the compiler would have no way of knowing whether to call it
|> or not when assigning the union.  Basically, although I don't think
|> that it is explicitly stated, it is implicit that union assignment
|> uses the equivalent of memcpy; this would break if one of the union
|> members had a user provided assignment operator.

Then that is clearly a good reason for not having a default assignment
operator for such unions, not for disallowing them in the first place!

--
---------------------------------------------------------------------
 Ed Osinski
 Computer Science Department, New York University
 E-mail:  osinski@cs.nyu.edu
---------------------------------------------------------------------
"No, no, no, don't tug on that. You never know what it might be attached to."
 -- Buckaroo Bonzai to assistant during brain surgery





Author: dsb@cs.duke.edu (Scott Bigham)
Date: 1995/07/22
Raw View
>In article <3u4hss$kmi@metro.ucc.su.OZ.AU>, John Max Skaller (maxtal@Physics.usyd.edu.au) writes:

>>                                    [...] I no longer care since
>>I have a set of templates supporting discriminated uniuons.

After some e-mail discussion with John, I am posting C source for a
program that generates the discriminated union templates John mentions
above.  John's template implementation defines template classes like
dunion3<T1,T2,T3>, which implements three-member discriminated unions.
Obviously you need different templates for different numbers of members;
my code outputs the appropriate template definitions for the requested
numbers of members.

I post this in the hopes that the community at large will find it
useful.  Enjoy.

      -sbigham
--
Scott Bigham                   | The opinions expressed above are
dsb@cs.duke.edu                | (c) 1995 Hacker Ltd. and cannot be
http://www.cs.duke.edu/~dsb/   | copied or distributed without a
      = PGP spoken here =      | Darn Good Reason(tm).

== mk_dunion.c ==================================================
/* mk_dunion -- discriminated union template code generator
 * 21 Jul 95 Scott Bigham (dsb@cs.duke.edu)
 */

#include <stdio.h>
#include <string.h>

/* Version log:
 *
 * 1.0 -- 19 Jul 95
 * Initial version
 *
 * 1.1 -- 21 Jul 95
 * Broke out the boilerplate text into a separate string array with
 * printf()-like markup to improve readability
 *
 */

/* mk_dunion generates definitions of class templates which provide the
 * functionality of discriminated unions.  The template implementation
 * itself is by John Max Skaller (maxtal@suphys.physics.su.oz.au); this
 * program generates instances of the implementation with the
 * appropriate numbers of members.
 *
 * Invoke the program with a list of numbers representing the numbers
 * of members you need:  for instance, `mk_dunion 2 3' would generate
 * code for template classes dunion2<T1,T2> and dunion3<T1,T2,T3>,
 * which implement two- and three-member discriminated unions,
 * respectively.  The generated code is written to stdout.
 */


/* The template implementation, with markup for the insertion of member
 * information.  The markup is interpreted as follows:
 *
 * - %N is replaced by the number of members.
 *
 * - @An (where n is a number) on a line by itself indicates that the
 *   next n lines are replicated once for each member, with an "index
 *   number" running from 1..#members.  Within those n lines, %i is
 *   replaced by the index number, and %p is replaced by the index
 *   number minus 1.
 *
 * - @an (where n is a number) is similar to @An, except the lines are
 *   replicated #members-1 times with index numbers 2..#members.
 *
 * - %A[...] and %a[...] are similar to @An and @an, except that the
 *   text between brackets is replicated the corresponding number of
 *   times on the same line, with %i and %p interpreted as above.
 */

const char *impl_lines[] = {
  "",
  "template<class type1%a[, class type%i]>",
  "class dunion%N",
  "{",
  "public:",
  "@A1",
  "  typedef type%i T%i;",
  "",
  "  enum tag_t {empty_tag=1%A[, T%i_tag] } tag;",
  "    // use 1 to enhance checking for corruption",
  "",
  "  void validate() const throw(bad_type) {",
  "    switch(tag) {",
  "      case empty_tag:",
  "@A1",
  "      case T%i_tag:",
  "        break;",
  "      default: throw bad_type(); break;",
  "    }",
  "  }",
  "",
  "  // default constructor",
  "  dunion%N() : tag(empty_tag) {}",
  "",
  "  // copy constructor",
  "  dunion%N(dunion%N const &u) : tag(u.tag)",
  "  {",
  "    u.validate();",
  "    switch(u.tag)",
  "    {",
  "@A1",
  "      case T%i_tag: new(data.buf) T%i(u.const_ref_T%i()); break;",
  "    }",
  "  }",
  "",
  "  // component constructors",
  "@A1",
  "  dunion%N(T%i const& t%i) : tag(T%i_tag) { new(data.buf) T%i(t%i); }",
  "",
  "  // destructor",
  "  ~dunion%N() { validate(); destroy_(); }",
  "",
  "  // accessors: unchecked",
  "@A4",
  "  T%i &ref_T%i() { return *((T%i*)(void*)data.buf); }",
  "  T%i const &const_ref_T%i() const { return *((T%i*)(void*)data.buf); }",
  "  T%i get_T%i() const { return *((T%i*)(void*)data.buf); }",
  "",
  "  // type retrieval",
  "  tag_t the_type() const { validate(); return tag; }",
  "",
  "  // type checks",
  "  bool is_type(tag_t t) const { validate(); return tag == t; }",
  "  bool is_empty() const { validate(); return tag==empty_tag; }",
  "@A1",
  "  bool is_T%i() const { validate(); return tag==T%i_tag; }",
  "",
  "  // type checks",
  "  void assert_is_type(tag_t t) const throw(type_fault)",
  "    { if(!is_type(t)) type_error(); }",
  "",
  "  void assert_is_empty() const throw(type_fault)",
  "    { if(!is_empty()) type_error(); }",
  "",
  "@A3",
  "  void assert_is_T%i() const throw(type_fault)",
  "    { if(!is_T%i()) type_error(); }",
  "",
  "  // checked accessors",
  "@A9",
  "  T%i &Ref_T%i() throw(type_fault)",
  "    { assert_is_T%i(); return ref_T%i(); }",
  "",
  "  T%i const &const_Ref_T%i() const throw(type_fault)",
  "    { assert_is_T%i(); return const_ref_T%i(); }",
  "",
  "  T%i Get_T%i() const throw(type_fault)",
  "    { assert_is_T%i(); return const_ref_T%i(); }",
  "",
  "  // assignment",
  "  void operator = (dunion%N const &a)",
  "  {",
  "    if(&a != this)",
  "    {",
  "      destroy_();",
  "      tag = a.tag;",
  "      switch (tag)",
  "      {",
  "        case empty_tag: break;",
  "@A1",
  "        case T%i_tag: new (data.buf) T%i(a.const_ref_T%i()); break;",
  "      }",
  "    }",
  "  }",
  "",
  "private:",
  "  void type_error() const throw (invalid_type)",
  "    { throw invalid_type(); }",
  "",
  "  enum",
  "  {",
  "    s1_ = sizeof(T1),",
  "    s1M_ = s1_,",
  "@a2",
  "    s%i_ = sizeof(T%i),",
  "    s%iM_ = (s%pM_ > s%i_ ? s%pM_ : s%i_),",
  "    size_ = s%NM_",
  "  };",
  "",
  "  struct dummy_t;",
  "  friend class dummy_t;",
  "  struct dummy_t",
  "  {",
  "    unsigned char buf[size_];",
  "  };",
  "  dummy_t data; // properly aligned for all T1%a[, T%i]",
  "",
  "  void destroy_()",
  "  {",
  "    switch(tag)",
  "    {",
  "      case empty_tag: break;",
  "@A1",
  "      case T%i_tag: destroy ( (T%i*)(void*) data.buf ); break;",
  "      default: type_error(); break;",
  "    }",
  "  }",
  "",
  "  friend ostream &operator << (ostream &out, dunion%N<T1%a[, T%i]> const &u)",
  "  {",
  "    typedef dunion%N<T1%a[, T%i]> u_t;",
  "",
  "    switch(u.the_type())",
  "    {",
  "      case u_t::empty_tag: { out << \"[Void Object]\"; } break;",
  "@A1",
  "      case u_t::T%i_tag: { out << u.const_ref_T%i(); } break;",
  "    }",
  "    return out;",
  "  }",
  "};",
  NULL
};

/* translate_repeater() -- partial expansion %A[...] and %a[...] markup.
   |start| and |len| define the text to be expanded; |whichrep| is the
   index number. */
void translate_repeater(const char *start, size_t len, int whichrep)
{
  register const char *s, *t;

  for (s = start, t = start + len; s < t; s++) {
    if (*s != '%') {
      fputc(*s, stdout);
      continue;
    }
    if (s + 1 == t) {
      fputc('%', stdout);
      return;
    }
    switch (s[1]) {
      case 'i':
 printf("%d", whichrep);
 break;;
      case 'p':
 if (whichrep > 1) {
   printf("%d", whichrep - 1);
   break;
 }
 /* else fall through */
      default:
 fputs("%p", stdout);
 break;
    }
    s++;
  }
}

/* translate_line() -- expand %N, %i, %p%, %A[...] and %a[...] in the
   line |line|.  |ntypes| is the number of members; |whichrep| is the
   index number if the line is part of a @An or @an expansion, or zero
   otherwise. */
void translate_line(const char *line, int ntypes, int whichrep)
{
  register const char *s = line, *t;
  register int i;

  while ((t = strchr(s, '%')) && t[1]) {
    fwrite(s, 1, t - s, stdout);
    switch (t[1]) {
      case 'N':
 printf("%d", ntypes);
 s = t + 2;
 continue;
      case 'i':
 if (whichrep > 0)
   printf("%d", whichrep);
 else
   fputs("%i", stdout);
 s = t + 2;
 continue;
      case 'p':
 if (whichrep > 1)
   printf("%d", whichrep - 1);
 else
   fputs("%p", stdout);
 s = t + 2;
 continue;
      case 'A':
      case 'a':
 if (t[2] != '[') {
   printf("%%%c", t[1]);
   s = t + 2;
   continue;
 }
 s = t;
 t = strchr(s + 2, ']');
 if (!t) {
   printf("%s\n", s);
   return;
 }
 for (i = (s[1] == 'a' ? 2 : 1); i <= ntypes; i++)
   translate_repeater(s + 3, (t - s) - 3, i);
 s = t + 1;
 continue;
      default:
 printf("%%%c", t[1]);
 s = t + 2;
 continue;
    }
  }
  printf("%s\n", s);
}

/* translate_lines() -- traverse the array of lines |lines|, handling
   @An and @an markup and otherwise expanding each line individually.
   |ntypes| is the number of members. */
void translate_lines(const char **lines, int ntypes)
{
  register const char **p;
  int n;
  register int i, j;

  for (p = lines; *p; p++) {
    if (!((*p)[0] == '@' && toupper((*p)[1]) == 'A')) {
      translate_line(*p, ntypes, 0);
      continue;
    }
    n = atoi(*p + 2);
    if (n == 0)
      continue;
    for (i = 1; i <= n; i++)
      if (!p[i]) {
 n = i - 1;
 break;
      }
    for (i = ((*p)[1] == 'a' ? 2 : 1); i <= ntypes; i++)
      for (j = 1; j <= n; j++)
 translate_line(p[j], ntypes, i);
    p += n;
  }
}

/* write_dunion() -- generate template code for an |ntypes|-member
   discriminated union template */
void write_dunion(int ntypes)
{
  translate_lines(impl_lines, ntypes);
}

/* main() -- I bet you can guess... ;) */
int main(int argc, char *argv[])
{
  int n, i;

  if (argc < 2) {
    fprintf(stderr, "usage: %s num [num ...]\n", argv[0]);
    exit(1);
  }

  fputs("// Generated by mk_dunion discriminated union template generator\n"
 "// Based on dunion<> template implementation by John Max Skaller\n"
 "\n#ifndef DUNION_H\n"
 "#define DUNION_H\n\n"
 "#include <iostream.h>\n"
 "#include <new.h>\n"
 "#include <bool.h>\n"
 "#include <defalloc.h>\n"
 "#include <typeinfo.h>\n"
 "\n"
 "struct type_fault { virtual ~type_fault(){} };\n"
 "struct invalid_type : type_fault {};\n"
 "struct bad_type : type_fault {};\n", stdout);

  for (i = 1; i < argc; i++) {
    n = atoi(argv[i]);
    if (n < 2)
      fprintf(stderr, "%s: invalid # of members: %s (must be >= 2)\n",
       argv[0], argv[i]);
    else
      write_dunion(n);
  }

  fputs("\n#endif // DUNION_H\n", stdout);
  return 0;
}





Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: 1995/07/19
Raw View
In article <3u32a8$lqn@snlsu1>,
Dom De Vitto <devitto@london.sinet.slb.com> wrote:
>Valentin Sliouniaev (vals@iol.ie) wrote:
>> Dear All,
>
>> Why is it impossible for a union to have a member with assignment
>> operator (as it is said in ARM)?
>
>Because I'm fairly sure unions can't have methods either. Only classes and
>stuctures can.
>

 Wrong. "A union is a class". Unions can have member functions
including constructors. The can't have or be bases, and they can't
have virtual members. (IMHO unnecessary restrictions).
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1995/07/18
Raw View
In article <3u1gjg$51g@barnacle.iol.ie> vals@iol.ie (Valentin
Sliouniaev) writes:

|> Why is it impossible for a union to have a member with assignment
|> operator (as it is said in ARM)?

Because the compiler would have no way of knowing whether to call it
or not when assigning the union.  Basically, although I don't think
that it is explicitly stated, it is implicit that union assignment
uses the equivalent of memcpy; this would break if one of the union
members had a user provided assignment operator.
--
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 en informatique industrielle --
                              -- Beratung in industrieller Datenverarbeitung







Author: beman@dawes.win.net (Beman Dawes)
Date: 1995/07/15
Raw View
In article <3u4hss$kmi@metro.ucc.su.OZ.AU>, John Max Skaller (maxtal@Physics.usyd.edu.au) writes:
>       I wrote a paper proposing this restriction be removed and
>the ISO C specification of unions "A union can contain ANY data type"
>be preserved (including, in this case, references).
>(The proposal is to shift the burden to the programmer in the case the
>compiler cannot generate assignment, a ctor or dtor)
>
>       The paper has not been considered. I no longer care since
>I have a set of templates supporting discriminated uniuons.
>The paper was prepared in response to a YES straw vote by ANSI
>to a proposal to ban references in unions -- and a significant opposition
>by WG21, which lead to the motion being withdrawn -- and a hole being
>left in the Working Paper.

That hole was closed yesterday in Monterey when the committee voted
to eliminate references from unions.  The (unofficial) X3J16 vote
count was forty something in favor of elimination, two against.
WG21 was four in favor or elimination, one opposed, one abstain.

-- Beman    (beman@dawes.win.net)





Author: devitto@london.sinet.slb.com (Dom De Vitto)
Date: 1995/07/17
Raw View
Stan Friesen (swf@elsegundoca.ncr.com) wrote:
> In article <3u32a8$lqn@snlsu1>, devitto@london.sinet.slb.com (Dom De Vitto) writes:
> |>
> |> Because I'm fairly sure unions can't have methods either. Only classes and
> |> stuctures can.

> They can have methods, though.

I stand corrected.  Learn sumthin' every day....
Thanks,
Dom





Author: devitto@london.sinet.slb.com (Dom De Vitto)
Date: 1995/07/13
Raw View
Valentin Sliouniaev (vals@iol.ie) wrote:
> Dear All,

> Why is it impossible for a union to have a member with assignment
> operator (as it is said in ARM)?

Because I'm fairly sure unions can't have methods either. Only classes and
stuctures can.

Anyway unions are upto the writer to sort out, as the type of the member
changes through the execution of the program, C++ can't know for sure.

If C++ can't know for sure, it can't call the right assignment op.

Dom





Author: bkline@cortex.nlm.nih.gov (Bob Kline)
Date: 1995/07/13
Raw View
Dom De Vitto (devitto@london.sinet.slb.com) wrote:
: Valentin Sliouniaev (vals@iol.ie) wrote:
: > Dear All,

: > Why is it impossible for a union to have a member with assignment
: > operator (as it is said in ARM)?

: Because I'm fairly sure unions can't have methods either. Only classes and
: stuctures can.

When was this restriction added?

/*----------------------------------------------------------------------*/
/* Bob Kline                                       Stream International */
/* bob_kline@stream.com               formerly Corporate Software, Inc. */
/* voice: (703) 522-0820 x-311                      fax: (703) 522-5407 */
/*----------------------------------------------------------------------*/





Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: 1995/07/14
Raw View
In article <3u1gjg$51g@barnacle.iol.ie>,
Valentin Sliouniaev <vals@iol.ie> wrote:
>Dear All,
>
>Why is it impossible for a union to have a member with assignment
>operator (as it is said in ARM)?
>

 Because Stroustrup and the committee did not think
unions worth the effort of extending to be full classes.

 The rationale is that a "memcpy" should be enough to
copy a union, and a "do nothing" enough to initialise or destroy one.
That is, the compiler doesn't need to know WHICH type is stored
in the union.

 I wrote a paper proposing this restriction be removed and
the ISO C specification of unions "A union can contain ANY data type"
be preserved (including, in this case, references).
(The proposal is to shift the burden to the programmer in the case the
compiler cannot generate assignment, a ctor or dtor)

 The paper has not been considered. I no longer care since
I have a set of templates supporting discriminated uniuons.
The paper was prepared in response to a YES straw vote by ANSI
to a proposal to ban references in unions -- and a significant opposition
by WG21, which lead to the motion being withdrawn -- and a hole being
left in the Working Paper.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189





Author: swf@elsegundoca.ncr.com (Stan Friesen)
Date: 1995/07/14
Raw View
In article <3u32a8$lqn@snlsu1>, devitto@london.sinet.slb.com (Dom De Vitto) writes:
|>
|> Because I'm fairly sure unions can't have methods either. Only classes and
|> stuctures can.

They can have methods, though.

--
swf@elsegundoca.attgis.com  sarima@netcom.com

The peace of God be with you.





Author: dqua@earthlink.net (Derick J.R. Qua-Gonzalez)
Date: 1995/07/14
Raw View
bkline@cortex.nlm.nih.gov (Bob Kline) writes:

>Dom De Vitto (devitto@london.sinet.slb.com) wrote:
>: Valentin Sliouniaev (vals@iol.ie) wrote:
>: > Dear All,

>: > Why is it impossible for a union to have a member with assignment
>: > operator (as it is said in ARM)?

>: Because I'm fairly sure unions can't have methods either. Only classes and
>: stuctures can.

>When was this restriction added?

There is no such restriction on a union; it may have methods, including
operator=. There are a few restrictions...



Author: vals@iol.ie (Valentin Sliouniaev)
Date: 1995/07/12
Raw View
Dear All,

Why is it impossible for a union to have a member with assignment
operator (as it is said in ARM)?

--Val.