Let's say I have a struct
struct Vector3 {
float x;
float y;
float z;
};
Note that sizeof(Vector3)
must remain the same.
EDIT: I am interested in solutions without setters.
Not let's create an instance of that struct Vector3 pos
. How can I implement my struct so I can have something like this pos.xy = 10 // updates x and y
or pos.yz = 20 // updates y and z
or pos.xz = 30 // updates x and z
?
Here is a solution that has the desired syntax, and doesn't increase the size of the class. It is technically correct, but rather convoluted:
union Vector3 {
struct {
float x, y, z;
auto& operator=(float f) { x = f; return *this; }
operator float&() & { return x; }
operator const float&() const & { return x; }
operator float () && { return x; }
float* operator&() { return &x; }
} x;
struct {
float x, y, z;
auto& operator=(float f) { y = f; return *this; }
operator float&() & { return y; }
operator const float&() const & { return y; }
operator float () && { return y; }
float* operator&() { return &y; }
} y;
struct {
float x, y, z;
auto& operator=(float f) { z = f; return *this; }
operator float&() & { return z; }
operator const float&() const & { return z; }
operator float () && { return z; }
float* operator&() { return &z; }
} z;
struct {
float x, y, z;
auto& operator=(float f) { x = y = f; return *this; }
} xy;
struct {
float x, y, z;
auto& operator=(float f) { y = z = f; return *this; }
} yz;
struct {
float x, y, z;
auto& operator=(float f) { z = x = f; return *this; }
} zx;
};
Another which relies on owner_of
implemented here: https://gist.github.com/xymopen/352cbb55ddc2a767ed7c5999cfed4d31 which probably depends on some technically implementation specific (possibly undefined) behaviour:
struct Vector3 {
float x;
float y;
float z;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::xy);
v->x = v->y = f;
return *this;
}
} xy;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::yz);
v->y = v->z = f;
return *this;
}
} yz;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::zx);
v->z = v->x = f;
return *this;
}
} zx;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::zx);
v->x = v->y = v->z = f;
return *this;
}
} xyz;
};
The simple way is to provide setters for the combinations you want to set:
struct Vector3 {
float x = 0;
float y = 0;
float z = 0;
void set_xy(float v) {
x = v;
y = v;
}
};
int main(){
Vector3 pos;
pos.set_xy(42);
}
And if you need sizeof(Vector3)
to stay the same, thats the only way.
Just "for fun" this is how you can get pos.set_xy = 20;
literally:
struct two_setter {
float& one;
float& two;
void operator=(float v){
one = v;
two = v;
}
};
struct Vector3 {
float x = 0;
float y = 0;
float z = 0;
two_setter set_xy{x,y};
};
int main(){
Vector3 pos;
pos.set_xy = 42;
}
However, it has severe downsides. First it can have almost twice the size of the original Vector3
. Moreover, because the two_setter
stores references, Vector3
cannot be copied. If it would store pointers, copying would be possible, but then even more code would be required to get it right.
Alternatively it is possible to provide a xy
method that returns a proxy that assigns the two members. But I am not going into detail, because pos.xy() = 3;
looks really odd, has no advantage to pos.xy(3)
and you really should provide a setter (or just rely on the user making two assignments when they want to make two assignments ;).
TL;DR Use a method instead of trying to get a syntax that C++ does not support out of the box.
It is possible to create an empty struct inside Vector3
with an operator=()
that sets the variables of the outer struct. Of course for a variable to really take no space itself, you have to use [[no_unique_address]]
, which is only available since C++20. But here is an example of how it might work:
struct Vector3 {
[[no_unique_address]] struct {
auto &operator=(float val) {
Vector3 *self = (Vector3 *)(this);
self->x = val;
self->y = val;
return *this;
}
} xy;
// Add similar code for xz and yz
float x;
float y;
float z;
};
See it running on godbolt.org.
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