Properties in conventional C++

I have been thinking for a long time about how nice it would be to have properties in C++ as Eiffel has and now managed C# and managed C++ has. Being able to maintain an abstraction to use “field like syntax” with methods only makes sense. After all operators can be overloaded in the traditional sense with a new class, why not allow them to be “overloaded” so that the ugly world of getters and setters could be a thing of the past. Getters and setters are nothing more than a syntactic kludge because the language won’t let you simply use operator syntax with methods that take zero or one parameter. For those that would imply that they don’t like not knowing whether they are calling a function or not – too late conventional operator overloading in c++ already crossed that line. The debate for and against properties could rage on for hours but if you would really prefer:
object->setValue(object->getValue()+10)
to
object->Value+=10;

Then this blog entry isn’t for you. Anyways the mechanics of solving this problem in c++ are interesting as scope comes heavily into play. The only way to add this facility to c++ without changing the compiler or using a precompiler (blah) is to use operator overloading. Operator overloading using this method hides access to the scope of original member variables using a straight out of the box method which defeats the purpose. The best way I could come up with was to use templates (yeah!) to automate most of the process and have them expect to call function pointers to member methods for set and get. This unfortunately creates an extra “back pointer” per property so the overloaded template class can “find” the object it belongs to. It is possible to lose this pointer in ways which are platform specific but this is truly ugly and I prefer not to do it. The principle here is that the offset to the class itself from the imbedded property object are constant and to simply store one static holding this offset. As I said this is ugly and I prefer not to do it. I will include the ugly code in a later blog, but definitely prefer the cleaner technique that holds the pointer. Anyways enough talk – here’s the code:)

// property.h for c++ by Steve Riley
#include “stddef.h”
#include
template
t* readonlytightproperty::parentDelta=0;template class property
{
public:
operator =(const u& invalue){assert(parent);(parent->*setter)(invalue);return *this;}
operator +=(const u& invalue){*this=*this+invalue;return *this;}
operator -=(const u& invalue){*this=*this-invalue;return *this;}
operator *=(const u& invalue){*this=*this*invalue;return *this;}
operator /=(const u& invalue){*this=*this/invalue;return *this;}
operator %=(const u& invalue){*this=*this%invalue;return *this;}
operator ^=(const u& invalue){*this=*this^invalue;return *this;}
operator &=(const u& invalue){*this=*this&invalue;return *this;}
operator |=(const u& invalue){*this=*this|invalue;return *this;}
operator >=(const u& invalue){*this=*this>>invalue;return *this;}
operator ++(){*this=*this+1;return *this;}
operator –(){*this=*this-1;return *this;}
operator ++(int){u temp;temp=*this;*this+=1;return temp;}
operator –(int){u temp;temp=*this;*this-=1;return temp;}

operator u(){assert(parent);return (parent->*getter)();}
private:
t* parent;
u value;
property():parent(0){}
void init(t* par)
{
if(!parent)
parent = par;
else
assert(parent == par);
// assert means someone is trying to use same getter and setter
// for two variables or init has been called twice with different
// values

}
friend t;
};

template class readonlyproperty
{
public:
operator u(){assert(parent);return (parent->*getter)();}
private:
t* parent;
u value;
readonlyproperty():parent(0){}
void init(t* par)
{
if(!parent)
parent = par;
else
assert(parent == par);
// assert means someone is trying to use same getter and setter
// for two variables or init has been called twice with different
// values

}
friend t;
};

template class Property
{
public:
operator =(const u& invalue){return value=invalue; }
operator u(){return value;}
operator +=(const u& invalue){*this=*this+invalue;return *this;}
operator -=(const u& invalue){*this=*this-invalue;return *this;}
operator *=(const u& invalue){*this=*this*invalue;return *this;}
operator /=(const u& invalue){*this=*this/invalue;return *this;}
operator %=(const u& invalue){*this=*this%invalue;return *this;}
operator ^=(const u& invalue){*this=*this^invalue;return *this;}
operator &=(const u& invalue){*this=*this&invalue;return *this;}
operator |=(const u& invalue){*this=*this|invalue;return *this;}
operator >=(const u& invalue){*this=*this>>invalue;return *this;}
operator ++(){*this=*this+1;return *this;}
operator –(){*this=*this-1;return *this;}
operator ++(int){u temp;temp=*this;*this+=1;return temp;}
operator –(int){u temp;temp=*this;*this-=1;return temp;}
private:
u value;
};

template class readonlyProperty
{
public:
operator u(){return value;}
private:
u value;
};

Sample utilization is here:

#include #include “property.h”

class simple
{
public:
int p;
};

class sample
{
public:
sample(){p.init(this);g.init(this);}
int setp(int a) { p.value=a;return a;}
int getp(){return p.value;}
int getg(){return p.value;}
property p;
readonlyproperty g;
};

class sample2
{
public:
Property p;
};

int _tmain(int argc, _TCHAR* argv[])
{
sample2 test2;
test2.p=20;
sample test;
test.p = 30;
test.g;
return 0;
}

The code doesn’t do anything too useful just uses sets up the property and sets its value. Note that it allows both a readonly property and a standard property. Also I have provided two templates for classes to use before they specialize the getter and setters. This saves them the extra baggage of the backpointer until it is needed.

To set up simply wrap the class or builtin type in the template and define the getter and setter. The only inconvenience – besides the back pointer – is the init call to set up the property with the back pointer. Outside of this it should work well, and let you publically use field syntax without worries;) Note this implemetation is just a starting point and could use quite a bit of work.

Leave a Reply