Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implementing Properties In C++

0.00/5 (No votes)
2 Apr 2003 1  
Implementing properties on c++ objects

Introduction

This project tries to simulate the property behaviors that exist in C# (and other languages...) in C++ without using any extensions. Most libraries or compilers that implement properties in C++ use extensions such as Managed C++ or C++ Builder or they use set and get methods which look like normal methods rather than properties.

Let us first see what a property is. A property acts like a field or member variable from the point of view of the library user, but it accesses the underlying variable through read or write methods or set or get methods.

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

A foo;
    
cout <<  foo.Count;

The Count property actualy calls the get function to return the value of the required variable. One of the main advantage of using properties instead of directly accessing the variable is that you can set the property as read only (where you are only allowed to read the variable, not change it), write only or both read write, so let us implement it.

We need to be able to do the following.

//-- Will call the get function to get the underlying value

int i = foo.Count;

//-- Will call the set function to set the value

foo.Count = i;

So it is obvious that we need to overload the = operator for setting the variable and also the return type (which we will show a little later).

We will implement a class called property which will act exactly like a property, as the following example shows.

template<typename Container, typename ValueType, int nPropType>
    class property {}

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

So now we need to set a pointer to the set and get method 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;
    }

    //-- 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 = operator 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;
    }

    //-- Cast the property class to the internal type --

    operator ValueType()
    {
        assert(m_cObject != NULL);
        assert(Get != NULL);
        return (m_cObject->*Get)();
    }

private:
    //-- Pointer to the module that contains the property --

    Container* m_cObject;
    //-- Pointer to set member function --

    void (Container::*Set)(ValueType value);
    //-- Pointer to get member function --

    ValueType (Container::*Get)();};

So now let's examine each piece.

The following code just sets the Container pointer to a valid instance that contains the property we're going to access.

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

The following code sets a pointer to the get and set member functions of the container class. The only restrictions are that the set member function must take a single parameter and return void and the get member function must take no parameters 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;
}

The first operator in the following code is the = operator, which calls the set member of the container passing the value. The second operator makes the entire property class act as the ValueType so it returns the value returned by the get function.

//-- Overload the = operator 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)();
}

Now let's see how to use it.

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 addresses are passed to the property class as shown in the constructor of the PropTest object. The line property<PropTest,int,READ_WRITE> Count; says that we have a read & write property of type integer in class PropTest called Count. Now you can call Count as normal member variable even though you're really calling 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 though 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 properties you create an instance of the property as follows

property<PropTest,int,READ_ONLY > Count;

And for write only properties you would create an instance of the property as follows

property<PropTest,int,WRITE_ONLY > Count;

Note: If you set the property to read only and you try to assign to it, it will cause an assertion in debug builds. Similarly, if you set the property to write only and you try to read it an assertion will occur in debug builds.

Conclusion:

This shows how to implement properties in C++ using standard C++ without any extensions. Naturally, using set and get methods are more efficient because with this method you have a new instance of the property class per each property.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here