So I have a vec4 class that uses members x,y,z,w which you can access using
point.x point.y etc.
However I want to reuse this vec4 class to be my color class (it already supports scalar multiplication, operator overloading lots of other nice stuff)
I just want to be able to refer to the members using another notation:
color.r
color.g
color.b
etc.
Is there anyway I can do this using a macro or other syntactic sugar?
If you are using Visual Studio (and are sure that it is the only target IDE...) you can use the following:
#include <cassert>
union vec4
{
struct
{
float x;
float y;
float z;
float w;
};
struct
{
float r;
float g;
float b;
float a;
};
};
int main()
{
vec4 vec = { 0 };
vec.y = 10.0f;
assert(vec.g == 10.0f);
return 0;
}
It will yield warnings warning C4201: nonstandard extension used : nameless struct/union, though you can disable it.
EDIT: as it turns out gcc supports this extension as well.
Unless you have a good amount of common behavior between a vector and a color I think this is a bad idea. But since you asked, here is a possible way to do that.
If you make the x, y, z and w members private and provide accessor methods to get to them, then it is easy to provide two alternative ways to refer to the same member variables. For example:
class vec4 {
private:
float _x, _y, _z, _w;
public:
// vector getter/setters
float& x() { return _x; }
float& y() { return _y; }
float& z() { return _z; }
float& w() { return _w; }
// color getter/setters
float& r() { return _x; }
float& g() { return _y; }
float& b() { return _z; }
float& a() { return _w; }
};
vec4 my_color;
my_color.r() = 1.0f;
my_color.g() = 0.0f;
my_color.b() = 0.0f;
my_color.a() = 1.0f;
You can easily do that with standalone accessor functions:
struct vec4 { double x, y, z; };
double& get_r(vec4& v) { return v.z; }
// and so on
Related
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.
Since your type is standard-layout, I think the only legal way to do this, as per the C++ standard, is with a union that contains sub-objects with custom operator= definitions.
With a union, you're allowed to view the common-initial sequence of the active member, provided all types are standard-layout types. So if we carefully craft an object that shares the same common members (e.g. 3 float objects in the same order), then we can "swizzle" between them without violating strict-aliasing.
For us to accomplish this, we will need to create a bunch of members that all have the same data in the same order, in standard-layout type.
As a simple example, lets create a basic proxy type:
template <int...Idx>
class Vector3Proxy
{
public:
// ...
template <int...UIdx,
typename = std::enable_if_t<(sizeof...(Idx)==sizeof...(UIdx))>>
auto operator=(const Vector3Proxy<UIdx...>& other) -> Vector3Proxy&
{
((m_data[Idx] = other.m_data[UIdx]),...);
return (*this);
}
auto operator=(float x) -> Vector3Proxy&
{
((m_data[Idx] = x),...);
return (*this);
}
// ...
private:
float m_data[3];
template <int...> friend class Vector3Proxy;
};
In this example, not all members of m_data are used -- but they exist so that the "common-initial sequence" requirement is satisfied, which will allow us to view it through other standard-layout types within the union.
This can be built up as much as you need; float conversion for single-component operators, support for arithmetic, etc.
With a type like this, we can now build a Vector3 objects out of these proxy types
struct Vector3
{
union {
float _storage[3]; // for easy initialization
Vector3Proxy<0> x;
Vector3Proxy<1> y;
Vector3Proxy<2> z;
Vector3Proxy<0,1> xy;
Vector3Proxy<1,2> yz;
Vector3Proxy<0,2> xz;
// ...
};
};
Then the type can easily be used to assign to multiple values at once:
Vector3 x = {1,2,3};
x.xy = 5;
Or to assign components of one part to another:
Vector3 a = {1,2,3};
Vector3 b = {4,5,6};
a.xy = b.yz; // produces {5,6,3}
Live Example
This solution also ensures that sizeof(Vector3) does not change, since all proxy objects are the same size.
Note: It's not valid in C++ to use a union with anonymous structs, though some compilers support it. So although it might be tempting to rewrite this like:
union {
struct {
float x;
float y;
float z;
}; // invalid, since this is anonymous
struct {
...
} xy;
}
This is not valid in standard C++, and would not be a portable solution.
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?
Just add the necessary class member functions to do this:
struct Vector3 {
float x;
float y;
float z;
void update_xy(float value) { x = y = value; }
void update_yz(float value) { y = z = value; }
void update_xz(float value) { x = z = value; }
};
class vec3{
float x;
float y;
float z;
const vec3 & operator= (const vec3 &rvec3)
{
x = rvec3.x;
y = rvec3.y;
z = rvec3.z;
return *this;
} };
class vec2{
vec3 vetex;
vec3 normal;
const vec2 & operator = (const vec2 &rvec2)
{
vetex = rvec2.vetex;
normal= rvec2.normal;
return *this;
} };
the complier show the error "operator = " is a private member of 'vec3' . How could this happens?
Change to
class vec3{
float x;
float y;
float z;
public:
const vec3 & operator= (const vec3 &rvec3)
{
x = rvec3.x;
y = rvec3.y;
z = rvec3.z;
return *this;
} };
The operator method has become public and it is visible from external code.
Besides the definition of operator= in the class vec2 is unnecessary because it is default behaviour of operator for this class. By the way operator= in the vec3 is unnecessary too.
Maybe you should change from class to struct.
By default, class members in c++ are private.
To allow external code to see the operator, you need to specify it as public.
class vec3{
float x;
float y;
float z;
public:
const vec3 & operator= (const vec3 &rvec3)
{
x = rvec3.x;
y = rvec3.y;
z = rvec3.z;
return *this;
} };
I am interested in trying something where I create a custom type and then access to its members using dot semantics. For example:
Class A{ //simplified, omitting constructors and other methods
private:
float numbers[3];
public:
float x(){ return numbers[0]; }
float y(){ return numbers[1]; }
float z(){ return numbers[2]; }
}
So I can do something like this:
A a;
//do stuff to populate `numbers`
float x=a.x;
But I would also like to make the elements in numbers lvalues so I can do something like this:
A a;
a.y=5; //assigns 5 to numbers[1]
How can I do this setting method?
You can return a reference to allow assignment:
float & x(){ return numbers[0]; }
^
// usage
A a;
a.x() = 42;
You should also have a const overload, to allow read-only access to a const object:
float x() const {return numbers[0];}
^^^^^
// usage
A const a = something();
float x = a.x();
First. You made functions x, y and z but assigning them to float. This wouldn't work.
Second. Change these functions to return referencies:
class A{ //simplified, omitting constructors and other methods
private:
float numbers[3];
public:
float & x(){ return numbers[0]; }
float & y(){ return numbers[1]; }
float & z(){ return numbers[2]; }
};
...
A point;
float x = point.x();
point.x() = 42.0f;
There's another way: declare referencies as a members of class and initialize them in c-tor:
class A{ //simplified, omitting constructors and other methods
private:
float numbers[3];
public:
float & x;
float & y;
float & z;
A() : x( numbers[ 0 ] ), y( numbers[ 1 ] ), z( numbers[ 2 ] ) {}
};
...
A point;
float x = point.x;
point.x = 42.0f;
P.S. Pay an attention on comment, that gave #MikeSeymour
Not unless you actually have public variables named x, y and z.
Or you can return a reference and then do a.y() = 5
Consider this:
class Vec3
{
private:
float n[3];
public:
float& x;
float& y;
float& z;
Vec3(float x_, float y_, float z_) : x(n[0]), y(n[1]), z(n[2])
{
x = x_;
y = y_;
z = z_;
}
}
can I be sure that doing this:
Vec3 v(1,2,3);
cout<<reinterpret_cast<float*>(&v)[0]<<"\t";
cout<<reinterpret_cast<float*>(&v)[1]<<"\t";
cout<<reinterpret_cast<float*>(&v)[2]<<"\t";
will give me 1 2 3 by every compiler/OS that follows the standard?
No. For that to work, you'd need (at least) a standard-layout type. float& isn't, and therefore Vec3 isn't either. (9/7, first bullet).
As said in other answers, this won't work, because of the float&. See Standard Layout Classes and Trivially Copyable Types for a long explanation about standard layout.
You could consider a slightly different approach:
class Vec3
{
private:
float n[3];
public:
float& x() { return n[0]; }
float& y() { return n[1]; }
float& z() { return n[2]; }
Vec3(float x_, float y_, float z_)
{
x() = x_;
y() = y_;
z() = z_;
}
};
Thus,
Vec3 v(1,2,3);
cout<<reinterpret_cast<float*>(&v)[0]<<"\t";
cout<<reinterpret_cast<float*>(&v)[1]<<"\t";
cout<<reinterpret_cast<float*>(&v)[2]<<"\t";
Will print 1 2 3 for all compilers
Edit
You might also want to see What are Aggregates and PODs (1st answer) and What are Aggregates and PODs (2nd answer) for more precise information.
Nope. Your class is not POD, so there are no guarantees whatsoever, AFAIK.
I want to be able to create a type that has 3 floats (x,y,z). I have tried:
typedef struct
{
float x;
float y;
float z;
} Vertex;
But that didn't work.
Does this have to be declared somewhere where it can be seen by main? How would I go about creating getter methods and other methods for a type I have made?
How I'd do it in C++. See main() for example usage. N.B. This hasn't been compiled or tested.
#include <iostream>
class Vertex
{
public:
// Construction
Vertex(float x,float y, float z) : x_(x), y_(y), z_(z) {}
// Getters
float getX() const {return x_;}
float getY() const {return y_;}
float getZ() const {return z_;}
// Setters
void setX(float val) {x_ = val;}
void setY(float val) {y_ = val;}
void setZ(float val) {z_ = val;}
private:
float x_;
float y_;
float z_;
};
int main()
{
Vertex v(6.0f,7.2f,3.3f);
v.setZ(7.7f);
std::cout < "vertex components are " << v.getX() << ',' << v.getY() << ',' << v.getZ() << std::endl;
}
does this have to be declared somewhere where it can be seen by main?
Yes. Typically the class or struct is declared in a header file, which you #include in whatever translation unit (c file) you use it in.
Using C, this works for me
typedef struct { float x; float y; float z; } Vertex;
int main(void) {
Vertex a = {42, -42, 0};
if (a.x + a.y + a.z == 0) return 1; /* warning about comparing floating point values */
return 0;
}