Search code examples
c++crashunions

Program crashes inside the copy constructor of a discriminated union


So I've been trying to create a discriminated union for my project. Coming from C, I thought it would be trivial... ;)

The structure is named Message. It has a MessageType and an inner union which holds either a MessageGet or a MessageSet.

enum MessageType {
    MESSAGE_GET,
    MESSAGE_SET
};

struct MessageGet {
    std::string store_name;
    std::vector<uint8_t> key;
};

struct MessageSet {
    std::string store_name;
    std::vector<uint8_t> key;
    std::vector<uint8_t> value;
};

struct Message {
    MessageType type;

    uint64_t sender_id;    

    union U {
        U() : get() {}

        U(const U& other) {
            get = other.get;
            set = other.set;
        }

        ~U() {}

        U& operator=(const U& other) {
            set = other.set;
            get = other.get;
            return *this;
        }

        MessageGet get;

        MessageSet set;
    } as;

    Message() {}

    ~Message() {
        switch (type) {
            case MESSAGE_GET: {
                as.get.~MessageGet();
                break;
            }
            case MESSAGE_SET: {
                as.set.~MessageSet();
                break;
            }
        }
    }

    Message(MessageGet get, uint64_t sender_id) {
        type = MESSAGE_GET;
        as.get = get;
        sender_id = sender_id;
    }

    Message(MessageSet set, uint64_t sender_id) {
        type = MESSAGE_SET;
        as.set = set;
        sender_id = sender_id;
    }

    Message(const Message& other) {
        type = other.type;
        as = other.as;
        sender_id = other.sender_id;
    }

    Message& operator=(const Message& other) {
        type = other.type;
        as = other.as;
        sender_id = other.sender_id;
        return *this;
    }
};

The program crashes elsewhere in the code where I do something like this:

Message message(MessageGet {std::move(store_name), std::move(key)}, sender);
messages.push_back(message); // messages is a local std::vector<Message>

It doesn't crash with an exception - simply a crash.

I've managed to narrow it down to inside the Message copy constructor. After that, have no clue what causes this. Ideas would be appreciated.


Solution

  •     U(const U& other) {
            get = other.get;
            set = other.set;
        }
    

    This always sets set last, making the value of get invalid. When you set one of the members of a union, it invalidates all the others. That's what makes a union different from a struct.

    Where you have:

        as = other.as;
    

    You have to change that to copy only the valid member.