My blog has been moved to ariya.ofilabs.com.

Thursday, November 10, 2005

Property in C++

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>
class Property
{
public:
 Property(){ owner = 0; };
 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;
};

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 ) = 0;
};

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> max;
  Property<double> value;
};

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 :-P

2 comments:

marisi said...

I personally like this for a getter :
int posX = Button1.getLeft();

I think it's a nicer Getter because Left is better for an attribute name not a method ?? PS : And I adore camel style :-P

BTW, this post (Property in C++) should be a 1 credit subject in my semester (at my campus, 1 credit is for a laboratory-business) :-) Nice work, but, Object Pascal still Rocks in my Heart coz' i know nothing about C++. :=)

Vinay Khaitan said...

After reading the problem, the first thing came into my mind is that make min,max object. Then in the copy constructor of min,max class, just call a funtion pointer(stored as member var). the address of funtion pointer though needs to be intialized in constructor :)