Properties in C++
When I wrote 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 important such a thing is to C++ developers, I spent three days developing this article. I hope you will find it useful also.
What Properties Are
Properties are like variables that store data, but trigger events that fire when reading or writing data from them. In other words, a property is an interactive variable evaluating itself and having different values when reading and writing to it.
It’s easy to write a class using a language like C# that contains properties, but it seems impossible to do with C++ because the C++ complier doesn’t support properties the way that C# does. For this reason, I have written this article to show you how to write C++ classes having properties like those found in C#.
You will understand 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
Imagine that you need to write an object that represents a person. This object may contain data such as:
Full Name Age Year of birth Gender
It could be written using C++, as follows:
class Person { public: Person( ){} virtual ~Person( ){} private: //data members char m_fName[20]; char m_lName[20]; UINT m_YearOfBirth; bool m_bGender; };
Note: In most cases, you can’t define a data member as public to be used directly because the data member should be maintained through the business logic that is implemented by the object.
If you need to get or set the value of m_bGender, you need to implement methods as shown below.
class Person { public: Person( ){} virtual ~Person( ){} void SetGender(bool bGender) {m_bGender = bGender;} bool GetGender() {return m_bGender;} private: //data members char m_fName[20]; char m_lName[20]; UINT m_YearOfBirth; bool m_bGender; };
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 the name of the property you need. Also, a single property can support different data types. In other words, in the example, you can let Gender accept string and Boolean values to use it, as shown below.
Person.Gender = "Male";
or
Person.Gender = true;
Of course, life now became much easier with Properties and the code now also became more readable.
Property Declaration
Now, I will show you how to write properties. Start by writing the Gender property as shown below.
class Person { public: Person( ){} virtual ~Person( ){} Begin_Property(char*,Gender) __get(char*,Gender) _set(char*); _get(bool); _set(bool); __release(Gender) End_Property(Gender) private: //data members char m_fName[20]; char m_lName[20]; UINT m_YearOfBirth; bool m_bGender; };
Now, look at the code. I started with the Property definition by using the Begin_Property macro. It 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 needed to declare the events get(ers) and set(ers) that will fire each time it’s being used, as shown below.
// This will fire _set(bool) event Person.Gender = true; // this will fire _get(bool) event bool gender = Person.Gender ; //This will fire _set(char*) Person.Gender = "Male"; //This will fire _get(char*) printf("Gender :%sn",(char*)Person.Gender);
_get and _set are two macros that take one parameter that represent 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 of the “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 (this 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, you need to implement set(ers) and get(ers). You can do so in the same place as shown below.
. . . Begin_Property(char*,Gender) __get(char*,Gender) _set(char*) { //do something here return Gender; } _get(bool) { //do something here return iValue; } _set(bool) { //do something here return iValue; } __release(Gender) End_Property(Gender) . . .
You can implement it by using implementation macros, but before showing you how to use those macros, let me explain two other points: what macro __get is and why set(ers) should return a value. All you need to do in _get(char*) is just to return a pointer to it this way: “return Gender;”. This is what __get does. __get is the default getter of the Property; because “Gender” is a char* property, you used it in this way: “__get(char*,Gender)”.
Now, to the second question, why should set(ers) return value like get(ers)? Simply, in C++ set(ers) can act as get(ers), as shown below.
bool bGender = Person.Gender = true;
Now, implement the Gender Property by using Imp_set and Imp_get macros. Both macros take three parameters: data type, Class name, and property name, as shown below.
Imp_set(char*,Person,Gender) { PROPERTY_PROLOGUE(Person,Gender) if (!Gender) Gender = new char[7]; if (strlen(iValue)<6 ) { int result; if ((result=strcmp(iValue,"Male"))==0) pThis->m_bGender = true; else { if ((result=strcmp(iValue,"Female"))==0) pThis->m_bGender = false; } if(result==0) strcpy(Gender,iValue); } return Gender; } Imp_set(bool,Person,Gender) { PROPERTY_PROLOGUE(Person,Gender) if (!Gender) Gender = new char[7]; if (pThis->m_bGender = iValue) strcpy(Gender,"Male"); else strcpy(Gender,"Female"); return (bool)iValue; } Imp_get(bool,Person,Gender) { PROPERTY_PROLOGUE(Person,Gender) return pThis->m_bGender; }
Because set(ers) and get(ers) can’t access class members directly, you used the PROPERTY_PROLOGUE macro. It defines a pointer to the class of the property as shown above, called “pThis”.
Also, in class termination, you need to free the memory and any allocated resources being used by the property. This is done by using the function __release macro. __release is a default release macro and its job is done as shown below.
if (Gender) { delete Gender; Gender = NULL; }
You also can implement release events yourself by 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 had covered the subject as much as I can/ For any inquires feel free to email me. Thanks a lot.