Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #6177 > unrolled thread

unions

Started byRuben Safir <ruben@mrbrklyn.com>
First post2011-06-04 05:26 +0000
Last post2011-06-10 13:16 +0200
Articles 14 — 8 participants

Back to article view | Back to comp.lang.c++


Contents

  unions Ruben Safir <ruben@mrbrklyn.com> - 2011-06-04 05:26 +0000
    Re: unions Ian Collins <ian-news@hotmail.com> - 2011-06-04 17:38 +1200
      Re: unions Ruben Safir <ruben@mrbrklyn.com> - 2011-06-04 05:45 +0000
        Re: unions Ian Collins <ian-news@hotmail.com> - 2011-06-04 18:04 +1200
          Re: unions Ruben Safir <ruben@mrbrklyn.com> - 2011-06-04 06:13 +0000
    Re: unions Öö Tiib <ootiib@hot.ee> - 2011-06-04 04:54 -0700
    Re: unions Edek <edek.pienkowski@gmail.com> - 2011-06-04 19:59 +0200
      Re: unions Vaclav Haisman <v.haisman@sh.cvut.cz> - 2011-06-04 20:13 +0200
        Re: unions Edek <edek.pienkowski@gmail.com> - 2011-06-04 22:34 +0200
          Re: unions Vaclav Haisman <v.haisman@sh.cvut.cz> - 2011-06-05 07:29 +0200
    Re: unions Jorgen Grahn <grahn+nntp@snipabacken.se> - 2011-06-09 09:19 +0000
    Re: unions Keith <kbelder@gmail.com> - 2011-06-09 11:17 -0700
      Re: unions Victor Bazarov <v.bazarov@comcast.invalid> - 2011-06-09 14:29 -0400
      Re: unions Edek <edek.pienkowski@gmail.com> - 2011-06-10 13:16 +0200

#6177 — unions

FromRuben Safir <ruben@mrbrklyn.com>
Date2011-06-04 05:26 +0000
Subjectunions
Message-ID<iscfn1$keo$8@reader1.panix.com>
There are some things, that as I learned to program, I learned them, 
decided they suck and would never use them, and then put them away.  
unions are one of these things (static was another and complex #define 
yet another).

But I took this employment exam this week and 10% of the questions 
involved unions.  As I understood unions, union sub-objects just step on 
each other in binary in the allocated space where they exist (talk about 
a snake pit for potential bugs).  Am I missing something about them?  I 
started to have deep doubts about what I thought I knew.


union {
	double x;
	char a[10];
} myunion;

myunion.a[0] = 'A';
myunion.a[1] = 'A';
myunion.a[2] = 'A';
myunion.a[3] = 'A';

std::cout << myunion.x << std::end 
is going to spit out some binary translation of a double with 4 bytes of 
65 dec, whatever that is.

Ruben

[toc] | [next] | [standalone]


#6182

FromIan Collins <ian-news@hotmail.com>
Date2011-06-04 17:38 +1200
Message-ID<94tun8FivcU7@mid.individual.net>
In reply to#6177
On 06/ 4/11 05:26 PM, Ruben Safir wrote:
> There are some things, that as I learned to program, I learned them,
> decided they suck and would never use them, and then put them away.
> unions are one of these things (static was another and complex #define
> yet another).
>
> But I took this employment exam this week and 10% of the questions
> involved unions.  As I understood unions, union sub-objects just step on
> each other in binary in the allocated space where they exist (talk about
> a snake pit for potential bugs).  Am I missing something about them?  I
> started to have deep doubts about what I thought I knew.

That's about right.  You could view a union as a struct with all the 
members having an offset of zero.

We don't use them in C++ as much as in C.

> union {
> 	double x;
> 	char a[10];
> } myunion;
>
> myunion.a[0] = 'A';
> myunion.a[1] = 'A';
> myunion.a[2] = 'A';
> myunion.a[3] = 'A';
>
> std::cout<<  myunion.x<<  std::end
> is going to spit out some binary translation of a double with 4 bytes of
> 65 dec, whatever that is.

Maybe, the result is undefined.

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#6186

FromRuben Safir <ruben@mrbrklyn.com>
Date2011-06-04 05:45 +0000
Message-ID<iscgp0$keo$12@reader1.panix.com>
In reply to#6182
On Sat, 04 Jun 2011 17:38:47 +1200, Ian Collins wrote:

>  std::cout<<  myunion.x<<  std::end
>> is going to spit out some binary translation of a double with 4 bytes
>> of 65 dec, whatever that is.
> 
> Maybe, the result is undefined.

why?  Because it is hardware and OS dependent?

[toc] | [prev] | [next] | [standalone]


#6188

FromIan Collins <ian-news@hotmail.com>
Date2011-06-04 18:04 +1200
Message-ID<94u07jFivcU10@mid.individual.net>
In reply to#6186
On 06/ 4/11 05:45 PM, Ruben Safir wrote:
> On Sat, 04 Jun 2011 17:38:47 +1200, Ian Collins wrote:
>
>>   std::cout<<   myunion.x<<   std::end
>>> is going to spit out some binary translation of a double with 4 bytes
>>> of 65 dec, whatever that is.
>>
>> Maybe, the result is undefined.
>
> why?  Because it is hardware and OS dependent?

What if a double was bigger than 10?

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#6189

FromRuben Safir <ruben@mrbrklyn.com>
Date2011-06-04 06:13 +0000
Message-ID<isciee$keo$13@reader1.panix.com>
In reply to#6188
On Sat, 04 Jun 2011 18:04:35 +1200, Ian Collins wrote:


>> why?  Because it is hardware and OS dependent?
> 
> What if a double was bigger than 10?

Yeah - but - that's a damn bug, wasn't a choice, and don't DO that wasn't 
a choice either..

I don't even know why this construction was ever invented.

[toc] | [prev] | [next] | [standalone]


#6194

FromÖö Tiib <ootiib@hot.ee>
Date2011-06-04 04:54 -0700
Message-ID<4ff4056c-9fd4-408d-a87b-95e5abff1783@e21g2000vbz.googlegroups.com>
In reply to#6177
On Jun 4, 8:26 am, Ruben Safir <ru...@mrbrklyn.com> wrote:
> There are some things, that as I learned to program, I learned them,
> decided they suck and would never use them, and then put them away.  
> unions are one of these things (static was another and complex #define
> yet another).
>
> But I took this employment exam this week and 10% of the questions
> involved unions.  As I understood unions, union sub-objects just step on
> each other in binary in the allocated space where they exist (talk about
> a snake pit for potential bugs).  Am I missing something about them?  I
> started to have deep doubts about what I thought I knew.

The union is one of tools how to create C-style polymorphism.

In C++ you have a base class (like Being) from what you derive
subclasses (like Bird and Mammal). Subclasses can have different from
each other subobjects (Mammal has tits but does not have beak, Bird
has beak but lacks tits). In C you can do similar thing with union (by
putting beak and tits into union). That is left in C++ so the code and
interfaces stay compatible.

> union {
>         double x;
>         char a[10];
>
> } myunion;
>
> myunion.a[0] = 'A';
> myunion.a[1] = 'A';
> myunion.a[2] = 'A';
> myunion.a[3] = 'A';
>
> std::cout << myunion.x << std::end
> is going to spit out some binary translation of a double with 4 bytes of
> 65 dec, whatever that is.

Now that is as undefined behavior as is accessing tits of a bird. ;)

[toc] | [prev] | [next] | [standalone]


#6209

FromEdek <edek.pienkowski@gmail.com>
Date2011-06-04 19:59 +0200
Message-ID<isdrr3$urc$1@node2.news.atman.pl>
In reply to#6177
   Am I missing something about them?  I
> started to have deep doubts about what I thought I knew.
>

Other things aside, by the standard unions are the only
possibility to alias two non-char types without breaking
optimisations. Maybe void* is another and a couple of
other strange cases. Don't ask me about where it is in the
standard; I just know that it is a pain to find bugs
caused by wrong casting coupled with unpredictable optimisation
results (UB caused by casts really, unions are safe).

Edek

[toc] | [prev] | [next] | [standalone]


#6210

FromVaclav Haisman <v.haisman@sh.cvut.cz>
Date2011-06-04 20:13 +0200
Message-ID<4DEA75DF.6010402@sh.cvut.cz>
In reply to#6209
Edek wrote, On 4.6.2011 19:59:
>   Am I missing something about them?  I
>> started to have deep doubts about what I thought I knew.
>>
> 
> Other things aside, by the standard unions are the only
> possibility to alias two non-char types without breaking
> optimisations. Maybe void* is another and a couple of
> other strange cases. Don't ask me about where it is in the
> standard; I just know that it is a pain to find bugs
> caused by wrong casting coupled with unpredictable optimisation
> results (UB caused by casts really, unions are safe).
Unions are not safe. You cannot write using one union member and read using
another. It is only GCC extension that it works but the standard does not
allow it, AFAIK.

-- 
VH

[toc] | [prev] | [next] | [standalone]


#6213

FromEdek <edek.pienkowski@gmail.com>
Date2011-06-04 22:34 +0200
Message-ID<ise4sq$8e7$1@node2.news.atman.pl>
In reply to#6210
On 06/04/2011 08:13 PM, Vaclav Haisman wrote:
> Edek wrote, On 4.6.2011 19:59:
>>    Am I missing something about them?  I
>>> started to have deep doubts about what I thought I knew.
>>>
>>
>> Other things aside, by the standard unions are the only
>> possibility to alias two non-char types without breaking
>> optimisations. Maybe void* is another and a couple of
>> other strange cases. Don't ask me about where it is in the
>> standard; I just know that it is a pain to find bugs
>> caused by wrong casting coupled with unpredictable optimisation
>> results (UB caused by casts really, unions are safe).
> Unions are not safe. You cannot write using one union member and read using
> another. It is only GCC extension that it works but the standard does not
> allow it, AFAIK.
>

I think it here depends what is precisely meant. Code:

typedef union {
    short    _s;
    UU*     _ptr;
} UU;

1) Safe:

short ok(UU *p1, UU* p2)
{
    short tmp = p2->_s; // a
    p1->_ptr = p2; // b, by different type


    return tmp + p1->_ptr->_s; //c

// I think this is what I meant as legal
// all access by union
// compiler knows a),b),c) may alias, same type accessed (UU)
// which causes a reread of p2->_s at c), because it may have
// been overwritten by b). No warnings.

	movzwl	(%rsi), %eax  a)
	movq	%rsi, (%rdi)  b)
	addw	(%rsi), %ax   c) <--- reload of _s
	ret
// Note: no reordering either, in case rdi == rsi
}

2) Also safe

short alsoOk (UU* p1)
{
     UU u; // not an aliasing problem, local copy accessed
     u._ptr = p1[23]->_ptr;
     return u._s;

	movq	184(%rdi), %rax // Trivial
	ret

};

3)

short foo (UU *uu, int *p2)
{
// not allowed. Casting to union in main (below) does not mean
// the same as my above 'all access by union'

     short tmp = uu->_s;

     p2[42] = 0; // may overwrite uu->_s

     return tmp + uu->_s; // will compiler reread uu->_s? No...

	movzwl	(%rdi), %eax
	movl	$0, 168(%rsi)
	addl	%eax, %eax  // tmp + tmp really

}

The output of the below is:
$ ./main
Is 84
  should be: 42
$


#include <iostream>

using namespace std;

extern short foo (UU*,int*);

int main ()
{
     int * ints = new int[100];

     for (int i = 0; i < 100; i++) ints[i]=i;

     int& ourVal = ints[42];

     short shouldBe = ((UU*)&ourVal)->_s; // warns

     cout << "Is " << foo ((UU*)&ourVal, ints) // warns
          << endl;

     shouldBe += ((UU*)&ourVal)->_s; // warns

     cout << " should be: " << shouldBe << endl;

     delete [] ints;
     return -1;
}



Is the extension you are referring to calling foo when UU is

typedef union uu {
    short     _s;
    union uu  _ptr;
    int       _int;
};

? Then generated code is fine.

Correct? I'm not a language lawyer. BTW, the effect depends on whether
foo and main are compiled together or separately, and whether they are
compiled with -fPIC or not, and possibly other options.

Edek

[toc] | [prev] | [next] | [standalone]


#6228

FromVaclav Haisman <v.haisman@sh.cvut.cz>
Date2011-06-05 07:29 +0200
Message-ID<4DEB141E.7060402@sh.cvut.cz>
In reply to#6213
Edek wrote, On 4.6.2011 22:34:
> On 06/04/2011 08:13 PM, Vaclav Haisman wrote:
>> Edek wrote, On 4.6.2011 19:59:
>>>    Am I missing something about them?  I
>>>> started to have deep doubts about what I thought I knew.
>>>>
>>>
>>> Other things aside, by the standard unions are the only
>>> possibility to alias two non-char types without breaking
>>> optimisations. Maybe void* is another and a couple of
>>> other strange cases. Don't ask me about where it is in the
>>> standard; I just know that it is a pain to find bugs
>>> caused by wrong casting coupled with unpredictable optimisation
>>> results (UB caused by casts really, unions are safe).
>> Unions are not safe. You cannot write using one union member and read using
>> another. It is only GCC extension that it works but the standard does not
>> allow it, AFAIK.
>>
> 
> I think it here depends what is precisely meant. Code:
> 
> typedef union {
>    short    _s;
>    UU*     _ptr;
> } UU;
> 
> 1) Safe:
> 
> short ok(UU *p1, UU* p2)
> {
>    short tmp = p2->_s; // a
>    p1->_ptr = p2; // b, by different type
> 
> 
>    return tmp + p1->_ptr->_s; //c
> 
> // I think this is what I meant as legal
> // all access by union
> // compiler knows a),b),c) may alias, same type accessed (UU)
> // which causes a reread of p2->_s at c), because it may have
> // been overwritten by b). No warnings.
> 
>     movzwl    (%rsi), %eax  a)
>     movq    %rsi, (%rdi)  b)
>     addw    (%rsi), %ax   c) <--- reload of _s
>     ret
> // Note: no reordering either, in case rdi == rsi
> }
> 
> 2) Also safe
> 
> short alsoOk (UU* p1)
> {
>     UU u; // not an aliasing problem, local copy accessed
>     u._ptr = p1[23]->_ptr;
>     return u._s;
This is not fine. The _s member has not been written to.

> 
>     movq    184(%rdi), %rax // Trivial
>     ret
> 
> };
> 
> 3)
> 
> short foo (UU *uu, int *p2)
> {
> // not allowed. Casting to union in main (below) does not mean
> // the same as my above 'all access by union'
> 
>     short tmp = uu->_s;
> 
>     p2[42] = 0; // may overwrite uu->_s
> 
>     return tmp + uu->_s; // will compiler reread uu->_s? No...
> 
>     movzwl    (%rdi), %eax
>     movl    $0, 168(%rsi)
>     addl    %eax, %eax  // tmp + tmp really
> 
> }
> 
> The output of the below is:
> $ ./main
> Is 84
>  should be: 42
> $
> 
> 
> #include <iostream>
> 
> using namespace std;
> 
> extern short foo (UU*,int*);
> 
> int main ()
> {
>     int * ints = new int[100];
> 
>     for (int i = 0; i < 100; i++) ints[i]=i;
> 
>     int& ourVal = ints[42];
> 
>     short shouldBe = ((UU*)&ourVal)->_s; // warns
> 
>     cout << "Is " << foo ((UU*)&ourVal, ints) // warns
>          << endl;
> 
>     shouldBe += ((UU*)&ourVal)->_s; // warns
> 
>     cout << " should be: " << shouldBe << endl;
> 
>     delete [] ints;
>     return -1;
> }
> 
> 
> 
> Is the extension you are referring to calling foo when UU is
> 
> typedef union uu {
>    short     _s;
>    union uu  _ptr;
>    int       _int;
> };
> 
> ? Then generated code is fine.
> 
> Correct? I'm not a language lawyer. BTW, the effect depends on whether
> foo and main are compiled together or separately, and whether they are
> compiled with -fPIC or not, and possibly other options.
Incorrect, IMHO. When your code has UB it is broken. It might seem ok at
first or second glance but it is still broken. It might visibly break with
different compiler version, flags or platform.

-- 
VH

[toc] | [prev] | [next] | [standalone]


#6476

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2011-06-09 09:19 +0000
Message-ID<slrniv141f.gtj.grahn+nntp@frailea.sa.invalid>
In reply to#6177
On Sat, 2011-06-04, Ruben Safir wrote:
> There are some things, that as I learned to program, I learned them, 
> decided they suck and would never use them, and then put them away.  
> unions are one of these things (static was another and complex #define 
> yet another).

If you mean "static" as in "limiting visibility of functions to the
translation unit", then you're missing out on things. 

> But I took this employment exam this week and 10% of the questions 
> involved unions.

I hope the job didn't have to do with C++, or that you're exaggerating
the 10% figure. I can't remember the last time I used unions in C++.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .  .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#6511

FromKeith <kbelder@gmail.com>
Date2011-06-09 11:17 -0700
Message-ID<92e3c64e-d8e5-4dce-a994-0006f43c6416@35g2000prp.googlegroups.com>
In reply to#6177
On Jun 3, 10:26 pm, Ruben Safir <ru...@mrbrklyn.com> wrote:

> But I took this employment exam this week and 10% of the questions
> involved unions.  As I understood unions, union sub-objects just step on
> each other in binary in the allocated space where they exist (talk about
> a snake pit for potential bugs).  Am I missing something about them?  I
> started to have deep doubts about what I thought I knew.


I'm writing a stack based scripting language for a project,
and I'm using a union for the stack.  Each slot in the stack
can hold a value that can represent a number of different types;
an integer, a function pointer, a pointer to char, or a pointer
to another stack entry.

The union definition is something like this (simplified):

union xt {
  int i;
  void (*f)();
  char *c;
  xt *p;
}

Now, I understand this is absolutely undefined behavior not guaranteed
to
work on all compilers; but as a question of practicality, it does work
on the compilers and systems I need it to work on.

It avoids a LOT of really ugly casts, and seems to be the most
efficient
way to handle the requirements.  In this case, I'm using C++ to
replicate
the techniques I'd use if I was writing it in assembly... and that is
something that should be allowed (although discouraging it is
reasonable).

I'm bracing myself for universal condemnation of my approach...

[toc] | [prev] | [next] | [standalone]


#6513

FromVictor Bazarov <v.bazarov@comcast.invalid>
Date2011-06-09 14:29 -0400
Message-ID<isr3dv$tck$1@dont-email.me>
In reply to#6511
On 6/9/2011 2:17 PM, Keith wrote:
> On Jun 3, 10:26 pm, Ruben Safir<ru...@mrbrklyn.com>  wrote:
>
>> But I took this employment exam this week and 10% of the questions
>> involved unions.  As I understood unions, union sub-objects just step on
>> each other in binary in the allocated space where they exist (talk about
>> a snake pit for potential bugs).  Am I missing something about them?  I
>> started to have deep doubts about what I thought I knew.
>
>
> I'm writing a stack based scripting language for a project,
> and I'm using a union for the stack.  Each slot in the stack
> can hold a value that can represent a number of different types;
> an integer, a function pointer, a pointer to char, or a pointer
> to another stack entry.
>
> The union definition is something like this (simplified):
>
> union xt {
>    int i;
>    void (*f)();
>    char *c;
>    xt *p;
> }
>
> Now, I understand this is absolutely undefined behavior not guaranteed
> to
> work on all compilers; but as a question of practicality, it does work
> on the compilers and systems I need it to work on.

Can you elaborate on *what* is "absolutely undefined behavior"?  You 
haven't actually described any behavior.  You just said what you store 
in those units.

> It avoids a LOT of really ugly casts, and seems to be the most
> efficient
> way to handle the requirements.  In this case, I'm using C++ to
> replicate
> the techniques I'd use if I was writing it in assembly... and that is
> something that should be allowed (although discouraging it is
> reasonable).
>
> I'm bracing myself for universal condemnation of my approach...

What approach?

V
-- 
I do not respond to top-posted replies, please don't ask

[toc] | [prev] | [next] | [standalone]


#6545

FromEdek <edek.pienkowski@gmail.com>
Date2011-06-10 13:16 +0200
Message-ID<issud7$q6k$1@node2.news.atman.pl>
In reply to#6511
>
> The union definition is something like this (simplified):
>
> union xt {
>    int i;
>    void (*f)();
>    char *c;
>    xt *p;
> }
>
> Now, I understand this is absolutely undefined behavior not guaranteed
> to
> work on all compilers; but as a question of practicality, it does work
> on the compilers and systems I need it to work on.

I don't see why this should be undefined behaviour based on what you
wrote; I do not know how you intend to use the union, that may be
undefined, but in general using unions in C++ is not undefined
behaviour. As someone wrote in this thread before, if, for every
'instance', if you read the same thing as you wrote to it - ie.
write function pointer, read function pointer - it is just fine.

Otherwise there would be no point in using unions in C++, no?

I think the pattern could be:

struct {
     int actualType;

     union {
         type1 _t1;
         type2 _t2;
     } val;
};

Then, based on actualType, you can read safely the right type,
assuming you set the actualType to some enum value which is appropriate.
Maybe actualType could be avoided if the 'type' of the union can
be determined from context, e.g. you don't dump the union to string,
and you don't need to double check the 'type' either.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web