Implementing a Property in C++

Environment: Visual C++ 7.0, Windows XP sp1, Windows 2000 sp3

Abstract

This project tries to simulate the property behavior that exists in C# (and other languages...) in C++, without using any extensions. Most libraries or compilers that implement properties in C++ use extensions, as in Managed C++ or C++ Builder, or they use the set and get methods that look like normal methods, not properties.

Details

Let's first see what a property is. A property acts like a field or member variable, in that is uses the library, but it accesses the underlying variable through the read or write methods or the set or get methods.

For example, if I have class A and property Count, I can write the following code:

  A foo;

  cout  << foo.Count;

The Count actualy calls the get function that returns the value of the required variable. One of the main advantages of using a property instead of directly using the variable is that you can set the property as read-only (you are allowed only to read the variable without changing it), write-only, or both read and write. So, let's begin implementing it:

We need to be able to do the following:

    int i = foo.Count;    //-- Will call the get function to
                          //   get the underlying value --

    foo.Count = i;        //-- Will call the set function to set
                          //   the value --

So, it obvious that we need to overload the '=' sign to set the variable, and also the return type (which we will now have later).

We will implement a class called property, which will act exactly like a property, as in the following:

template<typename Container, typename ValueType, int nPropType>

class property {}

This Template class will represent our property. The Container is the type of class that will contain the variable, the set & get methods, and the property. ValueType is the type of the internal variable itself, and nPropType is the access type of the property read-only, write-only, or read/write.

Now we need to set a pointer to the set and get methods from the container class to the property and also override the '=' operator so that the property will act like the variable. So, let's see the full listing of the property class:

#define READ_ONLY 1
#define WRITE_ONLY 2
#define READ_WRITE 3

template <typename Container, typename ValueType, int nPropType>
class property
{
public:
property()
{
  m_cObject = NULL;
  Set = NULL;
  Get = NULL;
}
//-- This to set a pointer to the class that contain the
//   property --
void setContainer(Container* cObject)
{
  m_cObject = cObject;
}
//-- Set the set member function that will change the value --
void setter(void (Container::*pSet)(ValueType value))
{
  if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
    Set = pSet;
  else
    Set = NULL;
}
//-- Set the get member function that will retrieve the value --
void getter(ValueType (Container::*pGet)())
{
  if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
    Get = pGet;
  else
    Get = NULL;
}
//-- Overload the '=' sign to set the value using the set
//   member --
ValueType operator =(const ValueType& value)
{
  assert(m_cObject != NULL);
  assert(Set != NULL);
  (m_cObject->*Set)(value);
  return value;
}
//-- To make possible to cast the property class to the
//   internal type --
operator ValueType()
{
  assert(m_cObject != NULL);
  assert(Get != NULL);
  return (m_cObject->*Get)();
}
private:
  Container* m_cObject;  //-- Pointer to the module that
                         //   contains the property --
  void (Container::*Set)(ValueType value);
                         //-- Pointer to set member function --
  ValueType (Container::*Get)();
                         //-- Pointer to get member function --
};

So, now let's see it piece by piece:

In the following code, just set the Container pointer to a valid instance that will have the property:

void setContainer(Container* cObject)
{
  m_cObject = cObject;
}

In the following code, set a pointer to the get and set member functions of the container class. The only restriction is that the set must have a single parameter and return void and the get no paramter, but return the value type:

//-- Set the set member function that will change the value --
void setter(void (Container::*pSet)(ValueType value))
{
  if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
    Set = pSet;
  else
    Set = NULL;
}
//-- Set the get member function that will retrieve the value --
void getter(ValueType (Container::*pGet)())
{
  if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
    Get = pGet;
  else
    Get = NULL;
}

In the following code, the first operator is the '='; it calls the set member of the container and gives it the value. The second part is to make the entire property class act as the ValueType so it returns the return of the get function:

//-- Overload the '=' sign to set the value using the set member --
ValueType operator =(const ValueType& value)
{
  assert(m_cObject != NULL);
  assert(Set != NULL);
  (m_cObject->*Set)(value);
  return value;
}
//-- To make possible to cast the property class to the
//   internal type --
operator ValueType()
{
  assert(m_cObject != NULL);
  assert(Get != NULL);
  return (m_cObject->*Get)();
}

So, let's see how to use it now:

As shown below, the PropTest class implements a simple property called Count. The actual value will be stored and retrieved from the private member variable "m_nCount", through the get and set methods. The get and set methods can have any name as long as their address is passed to the property class as shown in the constructor of the PropTest, in the line "property<PropTest,int,READ_WRITE> Count; " that we have a read & write property of type integer in class PropTest called Count. Now you can call Count as a normal member variable, but in fact you call the set and get methods indirectly.

The initialization shown in the constructor of the PropTest class is necessary for the property class to work.

class PropTest
{
public:
  PropTest()
  {
    Count.setContainer(this);
    Count.setter(&PropTest::setCount);
    Count.getter(&PropTest::getCount);
  }
  int getCount()
  {
    return m_nCount;
  }
  void setCount(int nCount)
  {
    m_nCount = nCount;
  }
  property<PropTest,int,READ_WRITE> Count;


private:
  int m_nCount;
};

As shown below, you use the Count property as if it were a normal variable.

int i = 5,j;
PropTest test;
test.Count = i;    //-- call the set method --
j= test.Count;     //-- call the get method --

For read-only, you create an instance for the property as follows:

property<PropTest,int,READ_ONLY > Count;

For write-only, you create an instance for the property as follows:

property<PropTest,int,WRITE_ONLY > Count;
Note: If you set the property to read-only and you try to write to it, it will cause an assertion. The same will occur if you set the property to write-only and you try to read it.

Conclusion

This article showed you how to implement a property in C++, using standard C++ without any extension. Of course, directly using the set and get methods is more efficient because with this method you have a new instance of the property class per each property.

Downloads

Download demo project - 100 Kb