Properties in C++

Introduction

When I was writing a property using C#, I wondered why such a feature doesn't exist in C++? It was a challenging question and because I know how such a thing is important to C++ developers, I spent three days developing this article. I hope you will find it useful as I thought.

What Properties Are

Properties are like variables that store data, but they trigger events that fire while reading or writing data from it. In another words, property is an interactive variable evaluating itself and having different values while reading and writing to it.

It's easy to write a class using language like C# that contains properties but it seems impossible with C++. This is because C++ compiler doesn't support properties like C#. For this reason, I have written this article to show you how to write C++ classes having properties like that of C#.

This document will show you how to use the macros that were written to declare and implement properties. If you are an expert C++ developer and you need to understand how it works, you will not face any problem in reading the macros defined in "Properties.h".

Why Properties are Important

Let's imagine that we need to write an object that represents a person. This object may contain data like:

The disadvantage of using this method is that you need to know which method should be used to change the Gender. But with properties, life is better because to do the same thing all you need is to know is the name of the property you need. Also a single property can support different data types, in other words in our example we can let Gender accept string and Boolean values use it as shown below.

Person.Gender = "Male";

or

Person.Gender = true;

Of course life now becomes much easier with Properties and the code now also becomes more readable.

Property Declaration

Now I will show you how to write properties. Let's start with writing Gender property as shown below:

Now let's look at the code. I started with Property definition using Begin_Property macro which takes two parameters, property data type and property name. Because Gender property is a string property, it should be char*. After I started defining my property, I need to declare the events get(ers) and set(ers) which will fire each time it's being used as shown below:

_get and _set are two macros which take one parameter that represents the data types that can be accepted by the property. You can notice the data type of _set and _get events independent of the data type of property. In other words, although the data type "Gender" property is char*, it has bool getters and setters. In this way, it can accept as Boolean or string values as shown above.

The last two macros I used are _release to release the memory it allocates (will be covered in detail later) and End_Property to close property declaration and both macros take property name as a parameter.

Property Implementation

After declaring Properties, we need to implement set(ers) and get(ers). We can do so in the same place as shown below:

Or implement it using implementation macros, but before showing you how to use those macros, let's discuss two other points, what macro __get is? and why set(ers) should return value? Well, all we need to do in _get(char*) is just return a pointer to it this way "return Gender;" and this is what __get does. __get is the default getter of the Property and because "Gender" is a char* property, we used it in this way "__get(char*,Gender)".

Let's switch to the second question, why set(ers) should return a value like get(ers)? Simply in C++, set(ers) can act as get(ers) as shown below:

bool bGender = Person.Gender = true;

Now let's implement Gender Property using Imp_set and Imp_get macros. Both macros take three parameters data type, Class name and property name as shown below:

Because set(ers) and get(ers) can't access class members directly, we used PROPERTY_PROLOGUE macro. It defines a pointer to the class of the property as shown above called "pThis".

Also in class termination, we need to free the memory and any allocated resources being used by the property, this is the function __release macro. __release is a default release macro and its job is done as shown below:

if (Gender)
{
delete Gender;
Gender = NULL;
}

We can also implement release events ourselves using _release and Imp_release as shown below:

Imp_release(Person,Gender)
{
// Release allocated resources here.
}

The Demo Project

This project demonstrates how to write properties and how properties can affect each other. For example, in the person class, the YearOfBirth property changes the age value property and vice versa. Also you can use class as property datatype, for example, I used CTime as a datatype of "LeaveDate" and "ContractDate" properties.

Finally ... I hope I have covered the subject as much as I could. For any inquiries, feel free to contact me. Thanks a lot.

Share

About the Author

Dears I'm from Cairo, Egypt. I'm using Visual C++ since 1994 Also I worked on areas like VB, VB.Net, ASP.NET, C# ,Pocket pc applications. If you like any of my articles or there are any suggested changes that can the way I code, or write articles Please feel free to email me.

Comments and Discussions

I like the word you did here. I am making use of the properties method you mentioned. I made a few extra macro's for the begin_property item that allow the setting of default values, and allow for the export of the properties from a dll. I had to allow exporting for a project I am working on. Nice work though. Also, not all c++ code knows what a NULL is. To that end, at the top of Properties.h file, it should have a ifndef/endif macro stating exacting how to define a NULL.

As mentioned already, Visual Studio has a __declspec for properties, and so you mentioned that you can use this for portability. However, the following are not portable:

- the 'BYTE' datatype in Properties.h is Microsoft specific. (Just use 'char' or 'unsigned char' instead).

- the statment 'public:P_##name(){name##=NULL;}' assumes that the data type can be assigned NULL. With your implementation of properties, you cannot use std::vector as the type for a property.

- offsetof() acting on non-PODs is not standard and is infact undefined behavior. While it may seem to work in most situations, it is known to fail quite miserably when used to access fields of virtual base members. See:http://gcc.gnu.org/ml/gcc/2003-04/msg01076.html[^]http://gcc.gnu.org/ml/gcc/2003-04/msg00974.html[^]
The way you use the offset macro does not rely on virtual base members, so you are probably 'safe' on most popular compiler vendors. However, strictly speaking, your implementation of properties will generate undefined behavior.

There is also one more thing to note: the size of the class increases with each property added. This is because C++ requires that the size of each class is at least 1. (This is also necessary because you use the offsetof macro. If the class didn't have any size, you couldn't be sure where it would exist -- and thus offsetof would not work.) While this may be OK for classes with a small number of properties and a small number of instantiated objects, it could certainly lead to excess memory use in the case of a large number of properties and a large number of instatiated objects.

Properties are a popular concept, and there have been a number of proposals for C++0x related to properties. Right now though, it doesn't look like we're going to see properties added to C++0x.

- Kevin Hall

P.S. Visual Studio 6.0 is pretty old and is very non-standard. You should download the free Visual Studio 2005 Express Edition.