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:
- Full Name
- Age
- Year of birth
- Gender
It could be written using C++ as shown below:
class Person {
public:
Person( ){}
virtual ~Person( ){}
private:
char m_fName[20];
char m_lName[20];
UINT m_YearOfBirth;
bool m_bGender;
};
NOTE: In most cases, we can't define data member as public
to be used directly because data member should be maintained through the business logic which is implemented by the object.
Now if we need to get or set the value of m_bGender
, we 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:
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 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:
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:
char m_fName[20];
char m_lName[20];
UINT m_YearOfBirth;
bool m_bGender;
};
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:
Person.Gender = true; bool gender = Person.Gender ; Person.Gender = "Male"; printf("Gender :%s\n",(char*)Person.Gender);
_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:
.
.
.
Begin_Property(char*,Gender)
__get(char*,Gender)
_set(char*)
{
return Gender;
}
_get(bool)
{
return iValue;
}
_set(bool)
{
return iValue;
}
__release(Gender)
End_Property(Gender)
.
.
.
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:
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, 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)
{
}
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.
History
- 20th December, 2006: Initial post