Field variables inside an Unnamed struct and union - c++

What is the meaning of having some fields inside a struct inside a union in C++? I found this from a piece of code from the "Math for game developers" video series in YouTube:
private:
union {
struct {
float m_x,m_Y;
};
};

I have checked out revision 2d964cbb9e5065ec327ef6e2a5f086820ed024c1 and grepped for union and the closest match to your code sample I could find was in math/vector.h following line 56.
Cut down to the construct in question, we basically have this. (Note that this is different from the code you are showing.)
struct vec3
{
union
{
struct
{
float x;
float y;
float z;
};
float v[3];
};
};
This will allow us to refer to the elements of a vec3 either by name, as in
std::ostream&
operator<<(std::ostream& os, const vec3& v3)
{
os << "(" << v3.x << ", " << v3.y << ", " << v3.z << ")";
return os;
}
or using array syntax as in
std::ostream&
operator<<(std::ostream& os, const vec3& v3)
{
os << "(";
for (std::size_t i = 0; i < 3; ++i)
os << (i ? ", " : "") << v3.v[i];
os << ")";
return os;
}
While this is about as good as you can get in C, personally, I think this is poor style C++. A more modern cleaner and equally efficient approach would use inline accessor functions and overload operator[].
#include <cstddef>
#include <stdexcept>
#ifndef NDEBUG
# define DEBUG 1
#else
# define DEBUG 0
#endif
class vec3
{
private:
float data_[3];
public:
constexpr vec3() noexcept : data_ {0.0f, 0.0f, 0.0f}
{
}
constexpr vec3(const float x, const float y, const float z) noexcept : data_ {x, y, z}
{
}
const float& x() const noexcept { return this->data_[0]; }
const float& y() const noexcept { return this->data_[1]; }
const float& z() const noexcept { return this->data_[2]; }
float& x() noexcept { return this->data_[0]; }
float& y() noexcept { return this->data_[1]; }
float& z() noexcept { return this->data_[2]; }
const float&
operator[](const std::size_t i) const noexcept(!DEBUG)
{
if (DEBUG && i >= 3)
throw std::invalid_argument {"vector index out of range"};
return this->data_[i];
}
float&
operator[](const std::size_t i) noexcept(!DEBUG)
{
if (DEBUG && i >= 3)
throw std::invalid_argument {"vector index out of range"};
return this->data_[i];
}
};
While this is arguably a little redundant, it will pay off by giving us a very clean and efficient interface.
Using explicit member access:
std::ostream&
operator<<(std::ostream& os, const vec3& v3)
{
os << "(" << v3.x() << ", " << v3.y() << ", " << v3.z() << ")";
return os;
}
Using array syntax (optionally range-checked):
std::ostream&
operator<<(std::ostream& os, const vec3& v3)
{
os << "(";
for (std::size_t i = 0; i < 3; ++i)
os << (i ? ", " : "") << v3[i];
os << ")";
return os;
}

Related

Getting error when overloading << operator

I have a Class called "Vector". It consists of two private fields: std::vector<double> coordinates and int len. Methoddim() returns len.
I am overloading operator << like that:
friend std::ostream& operator<<(std::ostream& os, Vector& vec )
{
std:: cout << "(";
for ( int i = 0; i < vec.dim(); i++ ) {
if ( i != vec.dim()-1){
os << vec[i] << ", ";
} else {
os << vec[i];
}
}
os << ')';
return os;
}
An operator + like that:
friend Vector operator +(Vector& first, Vector& second)
{
if(first.dim() != second.dim()){
throw std::length_error{"Vectors must be the same size"};
}else {
Vector localVec(first.dim()); // same as {0,0,0...,0} - first.dim() times
for (int i = 0; i < first.dim(); i++){
localVec[i] = first[i] + second[i];
}
return localVec;
}
}
And operator [] like that:
double& operator[](int index)
{
return this->coordinates[index];
}
And here's the problem:
Vector x{1,2,4};
Vector y{1,2,3};
Vector z = x + y;
std:: cout << z; // it works perfectly fine - (2, 4, 7)
std:: cout << x + y; // it gives me an error
could not match 'unique_ptr<type-parameter-0-2, type-parameter-0-3>' against 'Vector'
operator<<(basic_ostream<_CharT, _Traits>& __os, unique_ptr<_Yp, _Dp> const& __p)
It seems to me that this error is related to parameter Vector& vec , but I don't know whether it's right and what should I do to fix it. If anyone could give me a hint (or tell me what I should read more about) - I would be very grateful.
Here's full code:
class Vector
{
private:
std::vector <double> coordinates;
int len;
public:
Vector(): len{0}, coordinates{} {};
Vector(std::initializer_list <double> coordinates_): len{static_cast <int>( coordinates_.size())}, coordinates{coordinates_} {}
Vector(int len) : len{len} {
for(int i = 0; i < len; i++){
coordinates.push_back(0);
}
}
int dim() const
{
return this->len;
}
double& operator[](int index)
{
return this->coordinates[index];
}
friend std::ostream& operator<<(std::ostream& os, Vector& vec )
{
std:: cout << "(";
for ( int i = 0; i < vec.dim(); i++ ) {
if ( i != vec.dim()-1){
os << vec[i] << ", ";
} else {
os << vec[i];
}
}
os << ')';
return os;
}
friend Vector operator +(Vector& first, Vector& second)
{
if(first.dim() != second.dim()){
throw std::length_error{"Vectors must be the same size"};
}else {
Vector localVec(first.dim());
for (int i = 0; i < first.dim(); i++){
localVec[i] = first[i] + second[i];
}
return localVec;
}
}
};
friend std::ostream& operator<<(std::ostream& os, Vector& vec)
This signature doesn't accept rvalues, and this is why the error happens for your temporary result here:
std:: cout << x + y;
Change the second parameter into const Vector& vec or provide an overload with an r-value reference parameter.
friend std::ostream& operator<<(std::ostream& os, const Vector& vec);
A temporary cannot bind to a non-const reference argument. You are missing const in at least two places. Most importantly here:
friend std::ostream& operator<<(std::ostream& os, const Vector& vec )
// ^^
And there should a const overload of operator[]

Efficient non-trivial initialization of multiple members in C++ initialization lists

Suppose I want to write a class that represents an immutable normalized (norm == 1) vector in two dimensions along with a mutable int:
#include <cmath>
#include <iostream>
double norm2d(double x, double y)
{
return sqrt(x*x + y*y);
}
class NormalizedVector {
public:
NormalizedVector(double x, double y, int some_int)
: m_x(x / norm2d(x, y)), m_y(y / norm2d(x, y)), m_some_int(some_int){};
void set_some(int i) { m_some_int = i; }
private:
const double m_x;
const double m_y;
int m_some_int;
friend std::ostream& operator<< (std::ostream& os, const NormalizedVector& v) {
return os << "(" << v.m_x << "," << v.m_y << ")" << " : " << v.m_some_int;
}
};
int
main() {
NormalizedVector v { 1, 1 , 42};
std::cout << v << std::endl;
v.set_some(23);
std::cout << v << std::endl;
return 0;
}
Is it in this situation possible to avoid calling norm2d(x, y) twice in the ctor while still using initialization lists? Note that default-constructing members and assigning them in the ctor's body is not possible because the vector components are const.
I am well aware that this MWE is not very realistic but in the past I have for several times stumbled upon a situation where one function (or "some code") was required to compute values for multiple members in the initialization list and where in-body-assigning them was not an option because the corresponding members where either const or not zero-cost-default-constructable.
Yes, you can have a private constructor that takes an additional parameter, and delegate to that.
class NormalizedVector {
public:
NormalizedVector(double x, double y, int some_int)
: NormalizedVector(x, y, some_int, norm2d(x, y)) {};
void set_some(int i) { m_some_int = i; }
private:
NormalizedVector(double x, double y, int some_int, double norm)
: m_x(x / norm), m_y(y / norm), m_some_int(some_int){};
const double m_x;
const double m_y;
int m_some_int;
friend std::ostream& operator<< (std::ostream& os, const NormalizedVector& v) {
return os << "(" << v.m_x << "," << v.m_y << ")" << " : " << v.m_some_int;
}
};

operator<<() not resolving from template due to inheritance

I'm working with a set of classes and my main code looks like this:
main.cpp
#include "calc.h"
int main() {
neg_inf nif;
pos_inf pif;
limit<double, infinity> l( 3.4, nif, pif, 2.2 )
std::cout << "value dx = " << l.value() << '\n'
<< "lower lim = " << l.lower() << '\n'
<< "upper lim = " << l.upper() << '\n'
<< "step_size = " << l.step() << '\n';
return EXIT_SUCCESS;
}
The expected output should be:
value dx = 3.4
lower lim = -inf
upper lim = inf
step_size = 2.2
Here are my classes:
calc.h
#pragma once
#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits>
struct infinity {
protected:
infinity() = default;
};
struct pos_inf : public infinity {
constexpr double operator()() { return std::numeric_limits<double>::infinity(); }
};
struct neg_inf : public infinity {
constexpr double operator()() { return -std::numeric_limits<double>::infinity(); }
};
std::ostream& operator<<( std::ostream& os, const pos_inf& inf );
std::ostream& operator<<( std::ostream& os, const neg_inf& inf );
template<typename dX, class bound>
class limit {
dX dx;
bound lowerBound;
bound upperBound;
double step_size;
public:
limit( dX x, bound lower, bound upper, double step = 1 ) :
dx{ x }, lowerBound{ lower }, upperBound { upper }, step_size { step }
{}
dX value() const { return dx; }
bound lower() const { return lowerBound; }
bound upper() const { return upperBound; }
double step() const { return step_size; }
};
calc.cpp
#include "calc.h"
std::ostream& operator<<( std::ostream& os, const pos_inf& inf ) {
// originally intended to do:
// return os << inf(); // but fails to compile
auto v = pos_inf()(); // this works
return os << v;
}
std::ostream& operator<<( std::ostream& os, const neg_inf& inf ) {
// same as above...
auto v = neg_inf()();
return os << v;
}
However in the main.cpp Visual Studio 2017 is generating this compiler error:
c:\***\main.cpp(33): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'bound' (or there is no acceptable conversion)
1> with
1> [
1> bound=infinity
1> ]
based on this line of code:
<< "lower lim = " << l.lower() << '\n'
and is failing from l.lower()
However if I do this in main:
#include "calc.h"
int main() {
neg_inf nif;
pos_inf pif;
std::cout << nif << '\n' << pif << '\n'
return EXIT_SUCCESS;
}
I am getting the output:
-inf
inf
This tells me that my operator<<() are working for the inherited structs, however when I pass it's parent type as a template argument and pass the derived types into the constructor of my limit class, the operator<<() are not resolving. It appears to be an ambiguity problem but I'm not sure how to resolve this. What am I missing or overlooking here?
As a side note which is outside of this question, is there a more elegant way to represent -/+inf? I'm using inheritance here because + and - inf are not numbers but more of a concept, they are similar to each other but point in different directions. So when I pass an infinity type as a template argument I'd like to be able to set the lower bound to -inf and the upper bound to +inf. I want the bound type to be a template because I might want to use integer bounds or double bounds for example between [-1,1] or [0.0,1.0] in which these are all numeric bounds. I'm not sure how else to express infinity in a more elegant way and any tips or suggestions will be appreciated.
Do not overload operators for the subclasses that way. Use a virtual method to do the output and use the generic type with the overload operator that calls the virtual method:
class infinity {
public:
virtual ostream &printTo(ostream &o) const = 0;
};
ostream &operator<<(ostream &o,const infinity &i) {
return i.printTo(o);
}
class neg_inf : public infinity {
public:
virtual ostream &printTo(ostream &o) const {
// do what you want
return o;
}
};
Well, you have made overloads for operator<< taking const pos_inf& inf and const neg_inf& inf, but you are using infinity as the template type, thus your lower() method returns infinity. Of course the your operator overloads will not be used since they are derived types from infinity. Why not just overload the operator<< for infinity ?
Some quick ideas how to solve this:
Making the double operator()() virtual. But you can't mix that with constexpr.
Using template<typename dX, class lower_bound, class upper_bound> for limits class to actually specify the types for both bounds, then your lower and upper methods can return the pos_inf and neg_inf types and your current operators will work. Also, for simplicity you can also default the second type fo the first if the types will not always differ - template<typename dX, class lower_bound, class upper_bound = lower_bound>.
After giving more though about the design - why then not actually make the infinity class templated (since I assume you want it to match dX, and implement the limits there?
#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits>
template<typename T>
struct infinity {
public:
infinity() = default;
constexpr double lower()
{
return -std::numeric_limits<T>::infinity();
}
constexpr double upper()
{
return std::numeric_limits<T>::infinity();
}
};
template<typename dX>
class limit {
dX dx;
double step_size;
public:
limit(dX x, double step = 1) :
dx{ x }, step_size{ step }
{}
dX value() const { return dx; }
dX lower() const { return infinity<dX>().lower(); }
dX upper() const { return infinity<dX>().upper(); }
double step() const { return step_size; }
};
int main() {
limit<double> l(3.4, 2.2);
std::cout << "value dx = " << l.value() << '\n'
<< "lower lim = " << l.lower() << '\n'
<< "upper lim = " << l.upper() << '\n'
<< "step_size = " << l.step() << '\n';
return EXIT_SUCCESS;
}
Making lower/upper return dX. That way you actually leave the resolution from the bound type to your needed value type inside the template, and you can mix infinite and non-infinite limits.
#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits>
struct pos_inf {
constexpr operator double() const { return std::numeric_limits<double>::infinity(); }
};
struct neg_inf {
constexpr operator double() const { return -std::numeric_limits<double>::infinity(); }
};
template<typename dX, typename upper_bound = dX, typename lower_bound = dX>
class limit {
dX dx;
upper_bound lowerBound;
lower_bound upperBound;
double step_size;
public:
limit(dX x, upper_bound lower, lower_bound upper, double step = 1) :
dx{ x }, lowerBound{ lower }, upperBound{ upper }, step_size{ step }
{}
// with infinity these two will invoke operator double(), with actual double it will return the fixed value
dX lower() const { return lowerBound; }
dX upper() const { return upperBound; }
dX value() const { return dx; }
double step() const { return step_size; }
};
int main() {
limit<double, pos_inf, neg_inf> l(3.4, pos_inf(), neg_inf(), 2.2); // infinity
limit<double> l2(3.4, 1, 5, 2.2); // fixed values
std::cout << "value dx = " << l.value() << '\n'
<< "lower lim = " << l.lower() << '\n'
<< "upper lim = " << l.upper() << '\n'
<< "step_size = " << l.step() << '\n';
return EXIT_SUCCESS;
}
I think you are constraining yourself too much: you can drop the base class, add operator<< for both pos_inf and neg_inf and add an extra type to limit, in this way you can have the two bounds of different types. Here is what I mean:
Calc.h
#pragma once
#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits>
struct pos_inf {
constexpr double operator()() const { return std::numeric_limits<double>::infinity(); }
};
struct neg_inf {
constexpr double operator()() const { return -std::numeric_limits<double>::infinity(); }
};
// Both operators defined
std::ostream& operator<<(std::ostream& os, const pos_inf& inf);
std::ostream& operator<<(std::ostream& os, const neg_inf& inf);
//extra template type in limit
template<typename dX, class lowerBoundType, class UpperBoundType>
class limit {
dX dx;
lowerBoundType lowerBound;
UpperBoundType upperBound;
double step_size;
public:
limit(dX x, lowerBoundType lower, UpperBoundType upper, double step = 1) :
dx{ x }, lowerBound{ lower }, upperBound{ upper }, step_size{ step }
{}
dX value() const { return dx; }
lowerBoundType lower() const { return lowerBound; }
UpperBoundType upper() const { return upperBound; }
double step() const { return step_size; }
};
Calc.cpp
#include "calc.h"
std::ostream& operator<<(std::ostream& os, const pos_inf& inf) {
return os << inf(); // but fails to compile
}
std::ostream& operator<<(std::ostream& os, const neg_inf& inf) {
return os << inf(); // but fails to compile
}
main.cpp
#include "calc.h"
int main() {
neg_inf nif;
pos_inf pif;
limit<double, neg_inf, pos_inf> l(3.4, nif, pif, 2.2);
std::cout << "value dx = " << l.value() << '\n';
std::cout << "lower lim = " << l.lower() << '\n';
std::cout << "upper lim = " << l.upper() << '\n';
std::cout << "step_size = " << l.step() << '\n';
return EXIT_SUCCESS;
}
If this is not what you wanted, I apologize.
After taking the feedback from some who left answers and others who left comments and taking into consideration that the lower and upper bounds may not be of the same type, I had added in the extra template parameter. In this particular implementation it is unavoidable. However by doing so I was able to eliminate the need for inheritance altogether and just made two different structures one for each type. This also simplified my operator<<()s. So my classes now look like this:
calc.h
#pragma once
#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits>
struct neg_inf {
constexpr double operator()() { return -std::numeric_limits<double>::infinity(); }
};
struct pos_inf {
constexpr double operator()() { return std::numeric_limits<double>::infinity(); }
};
template<typename dX, class LowerBound, class UpperBound>
class limit {
dX dx;
LowerBound lowerBound;
UpperBound upperBound;
double step_size;
public:
limit( dX x, LowerBound lower, UpperBound upper, double step = 1 ) :
dx{ x }, lowerBound{ lower }, upperBound { upper }, step_size{ step }
{}
dX value() const { return dx; }
LowerBound lower() const { return lowerBound; }
UpperBound upper() const { return upperBound; }
double step() const { return step_size; }
};
calc.cpp
#include "calc.h"
std::ostream& operator<<(std::ostream& os, const neg_inf& inf) {
// not using the parameter, using constructor and its operator()
// since this is a function object or functor and returns a constexpr
return os << neg_inf()();
}
std::ostream& operator<<(std::ostream& os, const pos_inf& inf) {
// not using the parameter, using constructor and its operator()
// since this is a function object or functor and returns a constexpr
return os << pos_inf()();
}
Now in main, very similar as in my original but with the few modifications:
#include "calc.h"
int main() {
neg_inf nif;
pos_inf pif;
limit<double, neg_inf, pos_inf> l(3.4, nif, pif, 2.2);
std::cout << "value dx = " << l.value() << '\n'
<< "lower lim = " << l.lower() << '\n'
<< "upper lim = " << l.upper() << '\n'
<< "step_size = " << l.step() << '\n';
return EXIT_SUCCESS;
}
And this does work and gives me the output:
value dx = 3.4
lower lim = -inf
upper lim = inf
step_size = 2.2
Note However after thinking about this and getting it to work and comparing it with some of the other answers this does match, user's curiouslyrecurringthoughts answer.

c++ generic operator overload

How could I use operator overloading to add two objects without making it a member of of any object? Is this something to do with insertion operator overloading?
so instead of this, to use something more generic i.e. to be used with any objects?:
sameObjectType operator + ( const sameObjectType &x, const sameObjectType &y ) {
sameObjectType z;
z = x+y;
return z;
}
// I want to do the same for subtraction operatoR
sameObjectType operator - ( const sameObjectType &x, const sameObjectType &y ) {
sameObjectType z;
z = x-y;
return z;
}
You can get the idea from this sample code.
#include <iostream>
class One {
private:
int num1, num2;
public:
One(int num1, int num2) : num1(num1), num2(num2) {}
One& operator += (const One&);
friend bool operator==(const One&, const One&);
friend std::ostream& operator<<(std::ostream&, const One&);
};
std::ostream&
operator<<(std::ostream& os, const One& rhs) {
return os << "(" << rhs.num1 << "#" << rhs.num2 << ")";
}
One& One::operator+=(const One& rhs) {
num1 += rhs.num1;
num2 += rhs.num2;
return *this;
}
One operator+(One lhs, const One &rhs)
{
return lhs+=rhs;
}
int main () {
One x(1,2), z(3,4);
std::cout << x << " + " << z << " => " << (x+z) << "\n";
}

Having Public properties in c++ class

How do I have properties in C++ class, as you have in a C# class.
I don't want to have getter and setter methods.
You can use a solution similar to that Jon suggested, yet retaining ordinary C++ semantics using operator overloading. I've slightly modified Jon's code as following (explanations follow the code):
#include <iostream>
template<typename T>
class Accessor {
public:
explicit Accessor(const T& data) : value(data) {}
Accessor& operator=(const T& data) { value = data; return *this; }
Accessor& operator=(const Accessor& other) { this->value = other.value; return *this; }
operator T() const { return value; }
operator T&() { return value; }
private:
Accessor(const Accessor&);
T value;
};
struct Point {
Point(int a = 0, int b = 0) : x(a), y(b) {}
Accessor<int> x;
Accessor<int> y;
};
int main() {
Point p;
p.x = 10;
p.y = 20;
p.x++;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
We overload operator= to retain the usual assignment syntax instead of a function-call-like syntax. We use the cast operator as a "getter". We need the second version of the operator= to allow assignment of the second kind in main().
Now you can add to Accessor's constructor function pointers, or better - functors - to call as getters/setters in any way seems right to you. The following example assumes the setter function return bool to convey agreement to setting the new value, and the getter can just modify it on it's way out:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data)
{
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator()(const T& data)
{
return round(data, 2);
}
private:
double cint(double x) {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { value = getter(value); return value;}
operator T&() { value = getter(value); return value; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
However, as the last line demonstrates it has a bug. The cast operator returning a T& allows users to bypass the setter, since it gives them access to the private value. One way to solve this bug is to implement all the operators you want your Accessor to provide. For example, in the following code I used the += operator, and since I removed the cast operator returning reference I had to implement a operator+=:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data) const {
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator() (const T& data) const {
return round(data, 2);
}
private:
double cint(double x) const {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) const {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
private:
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { return getter(value);}
Accessor& operator+=(const T& data) { if (setter(value+data)) value += data; return *this; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
You'll have to implements all the operators you're going to use.
For behaviour that's kind of like this, I use a templated meta-accessor. Here's a highly simplified one for POD types:
template<class T>
struct accessor {
explicit accessor(const T& data) : value(data) {}
T operator()() const { return value; }
T& operator()() { return value; }
void operator()(const T& data) { value = data; }
private:
accessor(const accessor&);
accessor& operator=(const accessor&);
T value;
};
Typical usage is like this:
struct point {
point(int a = 0, int b = 0) : x(a), y(b) {}
accessor<int> x;
accessor<int> y;
};
point p;
p.x(10);
p.y(20);
p.x()++;
std::cout << p.x();
The compiler typically inlines these calls if you set things up right and have optimisation turned on. It's no more of a performance bottleneck than using actual getters and setters, no matter what optimisations happen. It is trivial to extend this to automatically support non-POD or enumerated types, or to allow callbacks to be registered for whenever data are read or written.
Edit: If you prefer not to use the parentheses, you can always define operator=() and an implicit cast operator. Here's a version that does just that, while also adding basic "stuff happened" callback support:
Further Edit: Okay, totally missed that someone already made a revised version of my code. Sigh.
If you don't care that your C++ code won't compile with anything other than the Microsoft Visual C++ compiler, then you can use some of the compiler's non-standard extensions.
For instance, the following code will create a C#-like property called MyProperty.
struct MyType
{
// This function pair may be private (for clean encapsulation)
int get_number() const { return m_number; }
void set_number(int number) { m_number = number; }
__declspec(property(get=get_number, put=set_number)) int MyProperty;
private:
int m_number:
}
int main()
{
MyType m;
m.MyProperty = 100;
return m.MyProperty;
}
More information on this Microsoft-specific language extension is available here.
Here's a PoC implementation I did a while back, works nicely except that you need to set something up in the constructor for it to work nice and smoothly.
http://www.codef00.com/code/Property.h
Here's the example usage:
#include <iostream>
#include "Property.h"
class TestClass {
public:
// make sure to initialize the properties with pointers to the object
// which owns the property
TestClass() : m_Prop1(0), m_Prop3(0.5), prop1(this), prop2(this), prop3(this) {
}
private:
int getProp1() const {
return m_Prop1;
}
void setProp1(int value) {
m_Prop1 = value;
}
int getProp2() const {
return 1234;
}
void setProp3(double value) {
m_Prop3 = value;
}
int m_Prop1;
double m_Prop3;
public:
PropertyRW<int, TestClass, &TestClass::getProp1, &TestClass::setProp1> prop1;
PropertyRO<int, TestClass, &TestClass::getProp2> prop2;
PropertyWO<double, TestClass, &TestClass::setProp3> prop3;
};
and some usage of this class...
int main() {
unsigned int a;
TestClass t;
t.prop1 = 10;
a = t.prop1;
t.prop3 = 5;
a = t.prop2;
std::cout << a << std::endl;
return 0;
}
There are two annoyances with this approach:
You need to give the property a
pointer to its owning class.
The syntax to declare a property is a
bit verbose, but I bet I can clean
that up a bit with some macros
You don't. C++ doesn't support properties like C# does. If you want code to run on set/get, it will have to be a method.
Properties aren't supported in C++, but you can implement them:
1) By using templates
2) By making language extension and writing custom code preprocessor
Either approach won't be easy, but it can be done.
You could provide get and set methods that have similar names to the data members:
class Example
{
private:
unsigned int x_;
double d_;
std::string s_s;
public:
unsigned int x(void) const
{ return x_;}
void x(unsigned int new_value)
{ x_ = new_value;}
double d(void) const
{ return d_;}
void d(double new_value)
{ d_ = new_value;}
const std::string& s(void) const
{ return s_;}
void s(const std::string& new_value)
{ s_ = new_value;}
};
Although this comes close, as it requires using '()' for each member, it doesn't meet the exact functionality of properties that Microsoft Languages provide.
The closest match for properties is to declare the data members as public.
Here’s a bit crude and simple implementation using a preprocessor macro to effortlessly generate nested classes that provide the functionality of getters and setters with the nice and clean syntax as if they were actual variables. No templates or function pointers are used (if that’s a plus), although your compiled program will have as many (sub)classes of the name property_* as there are PROPERTIES declarations, and like in Evan Teran’s solution, you need to give the property a reference to the owning class in the constructor.
More operators (operator++, operator+=) should be easy to add.
(I guess the reference to the owning class is ok, despite the circularity...? (x.property_age.x.property_age ...))
#include <iostream>
#include <stdexcept>
#define PROPERTY(type, name, owner, get_exprs, set_exprs) \
friend class property_ ##name; \
class property_ ##name { \
owner & x; \
public: \
property_ ##name (owner & init): x(init) {} \
operator type () { \
get_exprs \
} \
type operator= (const type value) { \
set_exprs \
return value; \
} \
} name ;
int current_year = 2020;
class person {
int year_of_birth; // Integer is signed to demonstrate argument validation
public:
// Remember to add each property and *this for them
person(): year_of_birth(0), age(*this) {}
const int& born() { return year_of_birth; }
// Remember the semicolons
PROPERTY(int, age, person,
/*get:*/ return current_year - x.year_of_birth; ,
/*set:*/ if (value < 0) throw std::invalid_argument("person::age : age cannot be negative");
x.year_of_birth = current_year - value; )
};
int main() {
person alice, bob;
alice.age = bob.age = 28;
alice.age = alice.age + 5;
//alice.age = -7; //throws
// Apparently the compiler does nice implicit conversion from
// (the macro-generated class) 'property_age' to 'int'
std::cout << "Alice: born: " << alice.born() << ", age: " << alice.age << '\n'
<< "Bob: born: " << bob.born() << ", age: " << bob.age << '\n'
<< "The mean of their ages is: " << (alice.age + bob.age) / 2.0 << '\n';
return 0;
}