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?
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:
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);Cursor
union, otherwise it's syntactically impossible to invoke its destructor (barring template tricks);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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With