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.
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 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.
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.
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 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.
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 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.
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.