Union Abilities

closed account (zb0S216C)
I'm stepping into unknown territory here (like always). I'm fiddling around with unions today. So I decided to create a union that houses a single variable but one that holds 2 values. Internally, the union contains two members:

mPre_buf_) A pointer to an array of unsigned chars. This buffer is used to store the values.
mT1_data_) A pointer of type T1 that's used to read from the buffer.

Here's the implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
template <typename T1, const unsigned int tpNo_of_vals = 1>
union Multi_Value
{
    public:
        Multi_Value(const T1 &pT1_data = T1())
            : mPre_buf_(new unsigned char[(tpNo_of_vals * sizeof(T1))])
        {
            if(mPre_buf_)
            {
                #if defined DEBUG_MODE
                    std::cout << "Allocating " << (tpNo_of_vals * sizeof(T1)) << " Bytes\n";
                #endif

                for(unsigned int I(0); I < tpNo_of_vals; ++I)
                    mT1_data_ = new(mPre_buf_ + (I * sizeof(T1))) T1(pT1_data);
            }
        }

        ~Multi_Value(void)
        {
            // Delete the objects first.
            for(unsigned int I(0); I < tpNo_of_vals; ++I)
                (mT1_data_ + I)->~T1();

            delete [ ] mPre_buf_;
        }

    private:
        unsigned char *mPre_buf_;
        T1 *mT1_data_;

    public:
        bool operator == (const T1 &pData)
        {
            for(int I(0); I < tpNo_of_vals; ++I)
            {
                #if defined DEBUG_MODE
                    std::cout << "Checking for " << pData << " at address: 0x" << I << '\n';
                #endif
                if((*(mT1_data_ + I)) == pData)
                {
                    #if defined DEBUG_MODE
                        std::cout << "Found " << pData << " at address: 0x" << I << '\n';
                    #endif
                    return(true);
                }
            }

            return(false);
        }

        Multi_Value &operator () (const unsigned int &pSlot, const T1 &pData)
        { 
            *(mT1_data_ + pSlot) = pData; 
            return(*this); 
        }
};

Not the prettiest thing in the world, nor is it the safest, but it's only experimental. Now, the question(s) in hand:

1) Can you see any memory leaks?
2) Can you offer any advice?

Thanks :)

P.S: Please don't suggest the use of any Boost or STL equivalents -- I will ignore such suggestions.

Wazzak
Last edited on
You are assigning both union members and expecting both of them to retain their value... which is very very bad.

The general rule with unions is that once you write to one member, all other members are lost. You can only read the member last written to.

Why do you need a union for this anyway? Why have mPre_buf_ at all? couldn't this all be done with just mT1_data_?
closed account (zb0S216C)
Fair points, Disch.

Disch wrote:
You are assigning both union members and expecting both of them to retain their value... which is very very bad.

So mPre_buf_ is pointing to 1 location, while mT1_data_ is pointing to another? You have a point.

Disch wrote:
The general rule with unions is that once you write to one member, all other members are lost. You can only read the member last written to.

So I'm attempting to distort the rules of union's? Not good.

Disch wrote:
Why do you need a union for this anyway? Why have mPre_buf_ at all? couldn't this all be done with just mT1_data_?

As my title suggests, I was seeing what I could do with unions. And by the sounds of it, I was trying to do the wrong things.

Thanks for your reply, Disch. As always, much appreciated :)

Wazzak
Last edited on
So mPre_buf_ is pointing to 1 location, while mT1_data_ is pointing to another? You have a point.


It's more like mPre_buf_ and mT1_data_ share the same memory, so writing to one will overwrite the other. If they are both are meant to point to the same thing, you might not have an issue, but it's not guaranteed and is arguably bad practice to assume that's OK.


Really I've only seem unions used one of 2 ways:

1) as a shortcut to extract bytes in a larger word without bitshifting:

1
2
3
4
5
6
7
8
9
union
{
  u16 word;
  struct
  {
    u8 lowbyte;
    u8 highbyte;
  };
};


(but of course, while this works on most compilers, it is not guaranteed to work by the standard, so it's very "iffy" in my book)


2) For "event" type structures (as in SDL, SFML, etc):

1
2
3
4
5
6
7
8
9
10
struct Event
{
  int type;  // what kind of event?  keyboard?  mouse?  etc?
  union
  {
    struct Keyboard { ... };
    struct Mouse { ... };
    struct Etc { ... };
  };
};


unions really don't have much use outside of that.
closed account (zb0S216C)
Disch wrote:
If they are both are meant to point to the same thing, you might not have an issue, but it's not guaranteed and is arguably bad practice to assume that's OK.

Well, originally, mPre_buf_ was never meant to move or even access the data, and mT1_data_ was also never meant to move. I figured since mPre_buf_ never moved, it was safe to call placement new within the region pointed to by mPre_buf_.

Disch wrote:
unions really don't have much use outside of that.

Maybe I'm stretching them too far to the point where it's considered undefined behaviour?

Wazzak
Topic archived. No new replies allowed.