Almost ten years ago a friend of mine showed that he was happy to use
Visual Basic’s (and also later on Delphi’s, with Borland’s extensions to Pascal)
feature of object property, which allows one to set a property of an object
and have the object respond to the change. For example, this code:
Button1.Left = 43
will automagically also move that button to a new position, not only
changing Left to a new value. In C++, this is not achievable
because no function call is involved. Instead, the code must be
modified to something like:
Button1.SetLeft(43);
And we need to have another (getter) method to obtain the property’s
value:
int posx = Button1.Left();
while in VB and Delphi’s Pascal, one Left is enough.
Personally, I believe this is only syntax stuff. It doesn’t matter so much,
it even improves nothing. But just to make a rebuttal, I crafted a simple
example to show that it is also possible to implement such feature in C++.
The key here is that each property is an object. To cover all basic
data type, obviously template-based is a good choice:
template<class t="T">
class Property
{
public:
Property(){ owner = ; };
operator T(){ return data; }
Property( T dat ){ data = dat; }
void setup( Object* obj, std::string n ){ owner = obj; name = n; }
Property& operator=( T dat ){
bool changed = dat!=data; data = dat;
if(owner && changed) owner->propertyChanged(name);return *this; }
private:
T data;
Object* owner;
std::string name;
};
</class>
Later on, to wrap that setup() method, a simple macro magic is employed:
#define INIT_PROPERTY(x) (x).setup( this, #x )
The basic object system needs to have method to be called when its
properties are modified:
class Object
{
public:
virtual void propertyChanged( std::string name ) = ;
};
As you can guess already, propertyChanged is invoked
when a new value is assigned to the property. So the secret is here
is the overloaded assignment operator.
A hypotetical widget named Slider can be implemented as follows:
class Slider: public Object
{
public:
Slider();
virtual void propertyChanged( std::string name );
Property<int> min;
Property</int><int> max;
Property<double> value;
};
</double></int>
Properties of this Slider must be initialized in the constructor. This is
so that each will get a unique name and assigned to an object (simply
this) to which it will report when its value is changed.
With the INIT_PROPERTY macro, this is as convenient as:
Slider::Slider()
{
std::cout < < "Creating slider" << std::endl;
INIT_PROPERTY( min );
INIT_PROPERTY( max );
INIT_PROPERTY( value );
}
The job of propertyChanged is then to handle the situation when
one of the property has been changed:
void Slider::propertyChanged( std::string name )
{
if( name == "min" )
{
std::cout < < "Slider.min has been changed" << std::endl;
// do something
}
if( name == "max" )
{
std::cout << "Slider.max has been changed" << std::endl;
// do something
}
if( name == "value" )
{
std::cout << "Slider.value has been changed" << std::endl;
// do something
}
}
Almost nothing else is needed. Then, the code snippet shown below is
already comparable to how it is done in VB:
Slider slider;
slider.min = 1;
slider.max = 42;
slider.value = 8.3;
Althought is a “fake implementation”, at least I have convinced
my friend that C++ can have property.
Of course, this trick has some disadvantages. Properties needs to be
initialized in the class constructor, so more boilerplate code compared
to the case where this kind of feature is supported in the language itself.
No checking when setting a value means corner cases must be well taken care of.
Also, properties are object instances which are not so cheap. Comparing
the property using string is also not fast.
Infinite loop is even possible when it is not handled well.
I believe that some functors in combination with more template and macro
magic will even allow the redirection of reading and writing property to
the corresponding getter and setter methods. Left as an
exercise for the reader 😛