How to add properties to standard C++ classes

One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property. These are like data members except they can have preconditions imposed on them prior to getting or setting their value.

In C++ the general way to implement property like behaviour is to have a getter and/or setter member function. For the most part this suffices but there is an issue with this approach: you lose the syntax and semantics of a data member and, instead, have to deal with the syntax and semantics of a member function.

What do we mean by this? Let’s take a very simple example class called “account” that contains an int called “balance_”.

As it currently stands “balance_” is a public data member. Although this gives us access to “balance_” it’s uncontrolled – no preconditions can be imposed. This is bad OOP design. It means “account” has no control over the value of “balance_” and so cannot guarantee the value is sane. In other words we could set balance_ to any rogue value that may or may not be appropriate for what it represents. Let’s make a change to ensure this is no longer the case.

That’s it, now “balance_” is private so only “account” can change it. Of course, this isn’t much use if we do want the outside world to change the value of “balance_”. What we need is a way to get and set the value but in a way that “account” can ensure things are sane. Enter the getter and setter function.

Now we have functions getting and setting “balance_”, which means we can put additional code in there to ensure, for example, that when we set “balance_” it cannot be negative (no one wants one of those!). Let’s do just that.

Great, finally a class that contains a member that we can get and set but in a controlled way. It’s all good right? Well, yes and no. You see we are now stuck with using function syntax and semantics.

    1. This presents two issues:

      Syntax: When writing generic code we need to rely on a class implementing a get or set method; if it doesn’t the code won’t build.

    2. Semantics: You can’t freely use get and set functions in an expression.

    Let’s deal with syntax first. We are going to write a generic function that takes an object that models the pair concept and sets both their values (first and second) to a value.

    This will work very well with std::pair.

    Let’s say we wanted to implement our own pair object so we can implement some sanity checks. We could do this by aggregating std::pair.

    Now, let’s try using this code with our function…

    What’s happened here? Simple. It won’t compile because the function doesn’t know it has to call set_first and set_second. Major fail! 🙁
    Now, let’s take a look at the issue of Semantics. Consider this small code expression…

    If x and y were objects that represented special ints (a bounded int maybe) that implemented a set method. Would this still work? Of course not

    Let see what we must do to make it work.

    Notice how we are forced to use functions, which breaks the nice free semantics of using assignment operators? Even if we make set return the value it has just set this still doesn’t work very well.

    It’s just ugly and non-intuitive.

    I hope that I’ve managed to demonstrate that although the getter and setter functions do serve a purpose they are a poor replacement for direct access to a variable. At this point I hope you are wondering, “great, how would a property help us”? Let’s find out!

    First, to give us a starting point, let’s take a look at how a property could be defined using a language that has native support. We will use C# and re-implement the foo_pair class.

    How neat is that? This implements a “first” and “second” property. Notice how each property has a get and set clause? You can implement either or both of these to make a property read/write, read-only or write-only. In this case we’ve implemented both so the property is read/write. Implementing only get makes it read-only and implementing only set makes it write only. As you can see the set clause can perform validation.

    Notice there is no function syntax so there is no actual value passed in? Instead C# provide access to a special implicit variable call “value”, which will contain the actual value being set. The property allows for the sanity checks whilst preserving data member syntax and semantics.

    So, the question is, can we do this in C++? The answer is sort of. The fact is there is no specific native support for properties in standard C++ (although some compilers to have vendor specific extensions) but the language does give us the tools we need to implement our own. Of course, we will never get the nice neat solution like we have in C# because properties are not part of the C++ language but we can get a reasonable facsimile.

    Let’s look at the code that will allow us to implement properties in C++.

    Pretty scary eh? That’s ok – it’s macros and they normally are! This article isn’t going to teach you how to read or write macros but it’s not that hard and a

    good tutorial will explain all you need to know to follow this code.

    So, how does this work? We then have four macros to facilitate adding a property class to a host class. All we do is use these macros to both define and implement a property.

    Let’s take a look at what each macro does:

    property_ : this macro defines a property, which is really nothing more than a local class with the cast and assignment operators defined to call ‘get’ and ‘set’ respectively

    This macro takes the following parameters:

    * TYPE – this is the type of the property (for example int)

    * OWNR – the host (parent) class name, required to be made a friend of the property allowing unprotected access

    * NAME – this is the name of the property (for example “first” or “second” as per the example above)

    * IMPL – this is the code that defines get and/or set, use add either or both of the get_ and set_ macros here

    get_   : this macro defines the non-const get clause – you need to add your own logic to specialise its behaviour between a { and a }

    set_   : this macro defines the set clause – you need to add your own logic to specialise its behaviour between a { and a }

    xprop_ : this macro allows the parent class to access the raw value unchecked variable bypassing the get and set methods

    The astute reader may note that some operator semantics such as ++ and += will not work (my thanks to EE’s Dan Rollins for pointing that out). These can be added after the get and set macros in the property definition, as required. When you add them make sure they callset to ensure proper value validation. We could trivialise this by defining additional macros for the operators we wish to support. I’ll leave that as an exercise for the reader.

    The solution presented here tries to find a balance between simplicity and safety to provide property semantics – without proper language support there will always be a compromise we will need to make.

    That’s it – pretty simple really. Here is the canonical form for using these macros to add a property to a class.

    So, that’s it. With a few simple macros we’ve managed to add properties to C++. Ok, in reality using some macro trickery we’re just adding member classes with some specialised behaviour but in reality this is more or less what a property is anyway. These simple macros just simplify the process of adding the necessary boilerplate to the class to implement the property.

    Is there a cost of using this code? Not really. Everything is passed around as a reference and the size of the property class should be no larger than the variable it represents since it has no other members. As there is no dynamic polymorphism involved the compiler should be able to inline most (if not all) of this and, thus, optimise away the function calls involved. Of course, if the get or set are too complex this may not be the case but, then, this would be no different from implementing get or set function anyway.

    Even if you decide using this code isn’t for you, I do hope reading this article has, at least, given you an insight into another aspect of OOP development and design – one that is, sadly, missing from the current version (C++03) of C++.