patterncppMinor
C#-like class properties
Viewed 0 times
propertiesclasslike
Problem
I am currently developing a new breed of 3D engine for my upcoming thesis and I really liked the C# properties, but now I am on C++11 for obvious reasons. Since I couldn't find it elsewhere, I tried to write my own property mechanism that shall nicely integrate with C++.
I'd appreciate expert feedback on my class (or at least people who know C++11 much better than I do); after all I am working with C++ for just a few weeks now.
The usage of my
And all the other stuff you can think of...
While it is rather easy to accomplish the obvious (see above) of such a mechanism, it gets a little more complicated if you also want to take into account lambda expressions as well as rare constructs (where all three operator template parameters are of a different type) along the lines of:
This obviously requires the *operator already to be implemented on
I posted the update below, since there are quite a lot of changes. If no one has an idea how to get rid of using member variables for storing the getter/setter, I will drop this approach. It just has too much memory footprint as it is now. But maybe someone still has use for this class and for me it was also a chance to further play around with C++.
Adding accessors was easier said than done, but here it is:
```
template, class TSetter = boost::function>
struct PROPERTY
{
template friend struct PROPERTY; // makes this a friend to all instanciations
private:
TGetter m_Getter;
TSetter m_Setter;
T m_ValueIfNoAccessors;
T& get()
{
if(!m_Getter.empty())
return m_Getter();
else
return m_ValueIfNoAccessors;
}
const T& get() const
{
if(!m_Getter.empty())
return m_G
I'd appreciate expert feedback on my class (or at least people who know C++11 much better than I do); after all I am working with C++ for just a few weeks now.
The usage of my
PROPERTY class is like this:struct MyClass{
PROPERTY myProp;
}
MyClass m = MyClass();
m.myProp *= 3;
int val = m.myProp;And all the other stuff you can think of...
While it is rather easy to accomplish the obvious (see above) of such a mechanism, it gets a little more complicated if you also want to take into account lambda expressions as well as rare constructs (where all three operator template parameters are of a different type) along the lines of:
PROPERTY myMat;
PROPERTY myVec;
PROPERTY res = myMat * myVec;This obviously requires the *operator already to be implemented on
NgMatrix4. The property mechanism just forwards them.I posted the update below, since there are quite a lot of changes. If no one has an idea how to get rid of using member variables for storing the getter/setter, I will drop this approach. It just has too much memory footprint as it is now. But maybe someone still has use for this class and for me it was also a chance to further play around with C++.
Adding accessors was easier said than done, but here it is:
```
template, class TSetter = boost::function>
struct PROPERTY
{
template friend struct PROPERTY; // makes this a friend to all instanciations
private:
TGetter m_Getter;
TSetter m_Setter;
T m_ValueIfNoAccessors;
T& get()
{
if(!m_Getter.empty())
return m_Getter();
else
return m_ValueIfNoAccessors;
}
const T& get() const
{
if(!m_Getter.empty())
return m_G
Solution
The idea of encapsulating your properties with a template is not bad, but, you are are missing the power of properties' accessors. There is not much difference on using properties without been able to override them.
Accessors allow a property to "communicate" with its containning class,
and to add extra functuonality if the are declared "virtual".
In the following example, I try to implement "property" with inheritance & method pointers, instead of templates. I consider the template techique much better.
Please ignore the direct implementation of properties, and check, how the property, interacts with the container class and its members, and viceversa, in the accessors methods. Take a look to the class code related to the accesors.
(The example may not run, I didin't have the real code at hand, so I coded from memory)
Take a look to the Width and Height functions. They depend on the value of the properties, but, are part of the class.
I add a second example, its the same previous code, plus using a descendant container class, that validates that the first coordinate is always lesser than the second coordinate, by overriding the accesors.
```
// ------------
// "Example2.cpp"
// ------------
// inherits from "PropertyClass",
// not from "PropertyBaseClass"
class Area: PropertyClass
{
public:
IntegerPROPERTY* X1;
IntegerPROPERTY* Y1;
Accessors allow a property to "communicate" with its containning class,
and to add extra functuonality if the are declared "virtual".
In the following example, I try to implement "property" with inheritance & method pointers, instead of templates. I consider the template techique much better.
Please ignore the direct implementation of properties, and check, how the property, interacts with the container class and its members, and viceversa, in the accessors methods. Take a look to the class code related to the accesors.
(The example may not run, I didin't have the real code at hand, so I coded from memory)
// ------------
// "properties.hpp"
// ------------
// dummy class to contain properties
class PropertyBaseClass
{
public:
PropertyBaseClass() ;
~PropertyBaseClass() ;
} ;
class PROPERTY
{
protected:
PropertyBaseClass* Container;
public:
PROPERTY(PropertyBaseClass* AContainer) { Container = AContainer; }
~PROPERTY() { Container = NULL ; }
} ;
// real class with properties
class PropertyClass
{
protected:
List Properties;
public:
PropertyBaseClass() { Properties = List(); }
~PropertyBaseClass() { Properties = NULL; }
} ;
typedef
int (PROPERTY::*IntegerGetter)();
typedef
(PROPERTY::*IntegerSetter)(int AValue);
class IntegerPROPERTY
{
protected:
PropertyBaseClass* Container;
int Data;
IntegerGetter getter();
IntegerSetter setter();
public:
PROPERTY(PropertyBaseClass* AContainer) ;
~PROPERTY() ;
int getValue() ;
void setValue(int AValue) ;
} ;
// ------------
// ...
// ------------
// "properties.cpp"
// ------------
class PROPERTY
{
protected:
PropertyBaseClass* Container;
public:
PROPERTY(PropertyBaseClass* AContainer) { Container = AContainer; }
~PROPERTY() { Container = NULL ; }
} ;
IntegerPROPERTY::IntegerPROPERTY(PropertyBaseClass* AContainer, IntegerGetter AGetter, IntegerSetter ASetter)
{
this.Container = AContainer;
this.getter = AGetter;
this.setter = ASetter;
}
IntegerPROPERTY::~IntegerPROPERTY()
{
this.Container = NULL;
}
int IntegerPROPERTY::getValue()
{
int Result = 0;
if (this.getter != NULL)
{
Result = this.getter();
}
return Result;
}
void IntegerPROPERTY::setValue(int AValue)
{
if (this.setter != NULL)
{
this.setter(AValue);
}
}
// ------------
// ...
// ------------
// "Example1.cpp"
// ------------
// inherits from "PropertyClass",
// not from "PropertyBaseClass"
class Area: PropertyClass
{
public:
IntegerPROPERTY* X1;
IntegerPROPERTY* Y1;
IntegerPROPERTY* X2;
IntegerPROPERTY* Y2;
protected:
int PX1;
int PY1;
int PX2;
int PY2;
virtual int getX1();
virtual void setX1(int AValue);
virtual int getY1();
virtual void setX1(int AValue);
virtual int getX2();
virtual void setX2(int AValue);
virtual int getY2();
virtual void setY2(int AValue);
int Width();
int Height();
public:
Area() ;
virtual Prepare();
} ;
void Area::Prepare()
{
X1 = new IntegerPROPERTY(&this, &Area::getX1, &Area::setX1);
Y1 = new IntegerPROPERTY(&this, &Area::getY1, &Area::setY1);
X2 = new IntegerPROPERTY(&this, &Area::getX2, &Area::setX2);
Y2 = new IntegerPROPERTY(&this, &Area::getY2, &Area::setY2);
}
int Area::getX1()
{
int Result = PX1;
return Result;
}
void Area::setX1(int AValue)
{
PX1 = AValue;
}
int Area::getX2()
{
int Result = PX2;
return Result;
}
void Area::setX2(int AValue)
{
PX2 = AValue;
}
void Area::setY1(int AValue)
{
PY1 = AValue;
}
int Area::getY2()
{
int Result = PY2;
return Result;
}
void Area::setY2(int AValue)
{
PY2 = AValue;
}
int Area::Width()
{
//int Result = (PX2 - PX1);
int Result = (this->X2->getter() - X1->getter());
return Result;
}
int Area::Height()
{
//int Result = (PY2 - PY1);
int Result = (this->Y2->getter() - Y1->getter());
return Result;
}
int main(...)
{
Area MyArea = new Area();
MyArea->Prepare();
MyArea->X1->setter(3);
MyArea->X2->setter(5);
MyArea->Y1->setter(23);
MyArea->Y2->setter(25);
cout X1->getter();
cout Y1->getter();
cout X2->getter();
cout Y2->getter();
cout Width();
cout Height();
return 0;
}
// ------------Take a look to the Width and Height functions. They depend on the value of the properties, but, are part of the class.
I add a second example, its the same previous code, plus using a descendant container class, that validates that the first coordinate is always lesser than the second coordinate, by overriding the accesors.
```
// ------------
// "Example2.cpp"
// ------------
// inherits from "PropertyClass",
// not from "PropertyBaseClass"
class Area: PropertyClass
{
public:
IntegerPROPERTY* X1;
IntegerPROPERTY* Y1;
Code Snippets
// ------------
// "properties.hpp"
// ------------
// dummy class to contain properties
class PropertyBaseClass
{
public:
PropertyBaseClass() ;
~PropertyBaseClass() ;
} ;
class PROPERTY
{
protected:
PropertyBaseClass* Container;
public:
PROPERTY(PropertyBaseClass* AContainer) { Container = AContainer; }
~PROPERTY() { Container = NULL ; }
} ;
// real class with properties
class PropertyClass
{
protected:
List<PROPERTY> Properties;
public:
PropertyBaseClass() { Properties = List<PROPERTY>(); }
~PropertyBaseClass() { Properties = NULL; }
} ;
typedef
int (PROPERTY::*IntegerGetter)();
typedef
(PROPERTY::*IntegerSetter)(int AValue);
class IntegerPROPERTY
{
protected:
PropertyBaseClass* Container;
int Data;
IntegerGetter getter();
IntegerSetter setter();
public:
PROPERTY(PropertyBaseClass* AContainer) ;
~PROPERTY() ;
int getValue() ;
void setValue(int AValue) ;
} ;
// ------------
// ...
// ------------
// "properties.cpp"
// ------------
class PROPERTY
{
protected:
PropertyBaseClass* Container;
public:
PROPERTY(PropertyBaseClass* AContainer) { Container = AContainer; }
~PROPERTY() { Container = NULL ; }
} ;
IntegerPROPERTY::IntegerPROPERTY(PropertyBaseClass* AContainer, IntegerGetter AGetter, IntegerSetter ASetter)
{
this.Container = AContainer;
this.getter = AGetter;
this.setter = ASetter;
}
IntegerPROPERTY::~IntegerPROPERTY()
{
this.Container = NULL;
}
int IntegerPROPERTY::getValue()
{
int Result = 0;
if (this.getter != NULL)
{
Result = this.getter();
}
return Result;
}
void IntegerPROPERTY::setValue(int AValue)
{
if (this.setter != NULL)
{
this.setter(AValue);
}
}
// ------------
// ...
// ------------
// "Example1.cpp"
// ------------
// inherits from "PropertyClass",
// not from "PropertyBaseClass"
class Area: PropertyClass
{
public:
IntegerPROPERTY* X1;
IntegerPROPERTY* Y1;
IntegerPROPERTY* X2;
IntegerPROPERTY* Y2;
protected:
int PX1;
int PY1;
int PX2;
int PY2;
virtual int getX1();
virtual void setX1(int AValue);
virtual int getY1();
virtual void setX1(int AValue);
virtual int getX2();
virtual void setX2(int AValue);
virtual int getY2();
virtual void setY2(int AValue);
int Width();
int Height();
public:
Area() ;
virtual Prepare();
} ;
void Area::Prepare()
{
X1 = new IntegerPROPERTY(&this, &Area::getX1, &Area::setX1);
Y1 = new IntegerPROPERTY(&this, &Area::getY1, &Area::setY1);
X2 = new IntegerPROPERTY(&this, &Area::getX2, &Area::setX2);
Y2 = new IntegerPROPERTY(&this, &Area::getY2, &Area::setY2);
}
int Area::getX1()
{
int Result = PX1;
return Result;
}
void Area::setX1(int AValue)
{
PX1 = AValue;
}
int Area::getX2()
{
int Result = PX2;
return Result;
}
void Area::setX2(int AValue)
{
PX2 = AValue;
}
void Area::setY1(int AValue)
{
PY1 = AValue;
}
int Area::getY2()
{
int Result = PY2;
re// ------------
// "Example2.cpp"
// ------------
// inherits from "PropertyClass",
// not from "PropertyBaseClass"
class Area: PropertyClass
{
public:
IntegerPROPERTY* X1;
IntegerPROPERTY* Y1;
IntegerPROPERTY* X2;
IntegerPROPERTY* Y2;
protected:
int PX1;
int PY1;
int PX2;
int PY2;
virtual int getX1();
virtual void setX1(int AValue);
virtual int getY1();
virtual void setX1(int AValue);
virtual int getX2();
virtual void setX2(int AValue);
virtual int getY2();
virtual void setY2(int AValue);
int Width();
int Height();
public:
Area() ;
virtual Prepare();
} ;
class ValidArea: Area
{
protected:
virtual void setX1(int AValue);
virtual void setX1(int AValue);
virtual void setX2(int AValue);
virtual void setY2(int AValue);
public:
Area() ;
virtual Prepare();
} ;
void Area::Prepare()
{
X1 = new IntegerPROPERTY(&this, &Area::getX1, &ValidArea::setX1);
Y1 = new IntegerPROPERTY(&this, &Area::getY1, &ValidArea::setY1);
X2 = new IntegerPROPERTY(&this, &Area::getX2, &ValidArea::setX2);
Y2 = new IntegerPROPERTY(&this, &Area::getY2, &ValidArea::setY2);
}
void ValidArea::setX1(int AValue)
{
if (AValue > PX2)
{
PX1 = PX2;
PX2 = AValue;
}
else
{
PX1 = AValue;
}
}
void ValidArea::setX2(int AValue)
{
if (AValue < PX1)
{
PX1 = PX2;
PX2 = AValue;
}
else
{
PX1 = AValue;
}
}
void ValidArea::setY1(int AValue)
{
if (AValue > PY2)
{
PY1 = PY2;
PY2 = AValue;
}
else
{
PY1 = AValue;
}
}
void ValidArea::setY2(int AValue)
{
if (AValue < PX1)
{
PX1 = PX2;
PX2 = AValue;
}
else
{
PX1 = AValue;
}
}
int main(...)
{
Area MyArea = new Area();
MyArea->Prepare();
MyArea->X1->setter(3);
MyArea->X2->setter(5);
MyArea->Y1->setter(23);
MyArea->Y2->setter(25);
cout << "X1: " << MyArea->X1->getter();
cout << "Y1: " << MyArea->Y1->getter();
cout << "X2: " << MyArea->X2->getter();
cout << "Y2: " << MyArea->Y2->getter();
cout << "X2: " << MyArea->Width();
cout << "Y2: " << MyArea->Height();
return 0;
}Context
StackExchange Code Review Q#7786, answer score: 5
Revisions (0)
No revisions yet.