Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I make a union containing a vec3 object?

I can't seem to create a union where a member is or contains a glm::vec3 object (an object for representing a coordinate, containing 3 floats in this case). (source code for glm::vec)

It's used in the following code:

struct Event {
    enum Type{
        tRaw,
        tAction,
        tCursor,
    } type;
    union {
        SDL_Event raw;
        struct {
            uint16 actionID;
            bool released;
        } action;
        struct {
            glm::vec3 prevPos;
            glm::vec3 pos;
        } cursor; // offending object, compiles if this is removed
    } data;
};

Visual Studio gives me the following intellisense error.

"Error: the default constructor of "union Event::<unnamed>" cannot be referenced -- it is a deleted function"

If removed, the union compiles without any problems. What might be causing this problem, and is there anything I can do to remedy it?

like image 801
Anne Quinn Avatar asked May 29 '18 06:05

Anne Quinn


1 Answers

Once you have a nontrivial type in your union (so, one for which the language enforces "correct" initialization, i.e. constructor invocation), you have to write your constructors/destructors explicitly:

#include <SDL/SDL.h>
#include <glm/vec3.hpp>
#include <stdint.h>
#include <new>
#include <vector>

struct Event {
    enum Type{
        tRaw,
        tAction,
        tCursor,
    } type;
    struct Cursor {
        glm::vec3 prevPos;
        glm::vec3 pos;
    };
    union {
        SDL_Event raw;
        struct {
            uint16_t actionID;
            bool released;
        } action;
        Cursor cursor;
    };
    Event(const SDL_Event &raw) : type(tRaw) {
        new(&this->raw) SDL_Event(raw);
    }
    Event(uint16_t actionID, bool released) : type(tAction) {
        this->action.actionID = actionID;
        this->action.released = released;
    }
    Event(glm::vec3 prevPos, glm::vec3 pos) : type(tCursor) {
        new(&this->cursor) Cursor{prevPos, pos};
    }
    Event(const Event &rhs) : type(rhs.type) {
        switch(type) {
        case tRaw:      new(&this->raw) SDL_Event(raw); break;
        case tAction:   memcpy((void *)&action, (const void *)&rhs.action, sizeof(action)); break;
        case tCursor:   new(&this->cursor) Cursor(rhs.cursor);
        }
    }

    ~Event() {
        if(type == tCursor) {
            this->cursor.~Cursor();
        }
        // in all other cases, no destructor is needed
    }
};

int main() {
    // Construction
    Event ev(1, false);
    SDL_Event foo;
    Event ev2(foo);
    glm::vec3 pos;
    Event ev3(pos, pos);
    // Copy construction & destruction
    std::vector<Event> events;
    events.push_back(ev);
    events.push_back(ev2);
    events.push_back(ev3);
    events.clear();
    return 0;
}

Some notes:

  • I avoided the data member, opting instead for an anonymous union; this avoids much boilerplate, as otherwise I'd have to write these constructors inside the union (because it's the union's constructor that is deleted and has to be explicitly defined), and then add forwarders on the outside; it also greatly simplifies the writing of the destructor (again, it would have to be written inside the union, but the union doesn't know the external type; you can work around this, but it's tedious and verbose);
  • I had to name explicitly the Cursor union, otherwise it's syntactically impossible to invoke its destructor (barring template tricks);
  • I did not implement the assignment operator; it's not complicated, but honestly it's quite tedious. You can find a basic blueprint (check if same type; if same, do a regular assignment; otherwise, destroy active member and placement-new onto the new one) in the link I posted before in the comments.

All this being said, this stuff has been already implemented in a more generic manner in C++17 as std::variant, so, if you have a recent enough compiler, you may consider using it instead.

like image 134
Matteo Italia Avatar answered Nov 07 '22 04:11

Matteo Italia