
Property in C ++
Probably all fans of the C ++ language who used other languages, such as C #, are surprised: why are there no properties in the pluses? After all, this is really a convenient tool that allows you to fully control access to class members. Recently, I became interested in this issue. Thinking, looking through the Straustrup and finally googling, I came to the conclusion that property can be implemented using the language. I think many have already seen various implementations, for example, from microsoft , but for someone, I hope this will be an interesting discovery.
In the article you will find one of the possible options for implementing properties using templates.
To begin with, we decide what we want. But we want the property to be accessed as an ordinary field of an object through a dot (well, or ->), both for writing and reading, and without any brackets. That is, like this: In this case, the setter and getter functions defined by us should be called. I would also like for the property to be defined in the class as simple and clear as possible.
We see that if a.property is an instance of the Property class that we defined, then we can overload the = operator to get the setter, as well as the cast operator, to get the getter. This class itself must be boilerplate to allow the use of properties of any type.
In order not to implement a class for each property, overloaded operators must call the function using the pointer passed to the property in the constructor.
For use, we declare a variable of our type Property as a public field in the class and pass the type that we need as the first parameter of the template, and pointers to functions in the parameters of the constructor.
When all this was implemented, I remembered another great feature of the property: it can be Read Only. Prior to this, only a full-fledged property with a setter and getter was considered. And it is important that read and write permissions are checked at the compilation stage. Here, template specializations come to our aid. Three different classes can be implemented (read and write, read only, write only) Property with the same names, differing in the template parameter, the so-called specialization. The final implementation looks like this:
You can use it like this:
TestClass.h file
File TestClass.cpp
File main.cpp
This implementation suits me, but I do not like it for several reasons. The first is the need to specify the class to which the property belongs. The second is the need to initialize each property in the constructor, that is, at the execution stage. The third is the impossibility of creating a static property. Well, the fourth, less significant: the tip shows the Property type, and not what we write there, which may cause some questions from the one who first sees it. Also, the topic of pointers to class methods is very muddy, poorly disclosed and of little use. It is well explained here , but the most important thing is that I realized that you need to use them very carefully.
It turned out not so intuitive to use as I wanted, but the goal was achieved.
Used materials:
www.rsdn.ru/article/vcpp/props.xml - the main ideas.
www.rsdn.ru/forum/cpp/854559.1.aspx - an idea with access rights.
rsdn.ru/article/cpp/fastdelegate.xml - a detailed explanation of pointers to functions and methods of classes in C ++.
Sources with examples here .
Visual Studio 2008 project here .
This text is published under the CC-BY License .
In the article you will find one of the possible options for implementing properties using templates.
Formulation of the problem
To begin with, we decide what we want. But we want the property to be accessed as an ordinary field of an object through a dot (well, or ->), both for writing and reading, and without any brackets. That is, like this: In this case, the setter and getter functions defined by us should be called. I would also like for the property to be defined in the class as simple and clear as possible.
a.property = value;
MyClass var = a.property;
Implementation
We see that if a.property is an instance of the Property class that we defined, then we can overload the = operator to get the setter, as well as the cast operator, to get the getter. This class itself must be boilerplate to allow the use of properties of any type.
In order not to implement a class for each property, overloaded operators must call the function using the pointer passed to the property in the constructor.
For use, we declare a variable of our type Property as a public field in the class and pass the type that we need as the first parameter of the template, and pointers to functions in the parameters of the constructor.
When all this was implemented, I remembered another great feature of the property: it can be Read Only. Prior to this, only a full-fledged property with a setter and getter was considered. And it is important that read and write permissions are checked at the compilation stage. Here, template specializations come to our aid. Three different classes can be implemented (read and write, read only, write only) Property with the same names, differing in the template parameter, the so-called specialization. The final implementation looks like this:
#pragma once
#define NULL 0
class ReadOnly;
class WriteOnly;
class ReadWrite;
template
class Property
{
};
template
class Property
{
protected:
typedef Type (Owner::*getter)();
typedef void (Owner::*setter)(Type);
Owner * m_owner;
getter m_getter;
setter m_setter;
public:
// Оператор приведения типа. Реализует геттер.
operator Type()
{
return (m_owner->*m_getter)();
}
// Оператор присваивания. Реализует сеттер.
void operator =(Type data)
{
(m_owner->*m_setter)(data);
}
Property() :
m_owner(NULL),
m_getter(NULL),
m_setter(NULL)
{
}
Property(Owner * const owner, getter getmethod, setter setmethod) :
m_owner(owner),
m_getter(getmethod),
m_setter(setmethod)
{
}
void init(Owner * const owner, getter getmethod, setter setmethod)
{
m_owner = owner;
m_getter = getmethod;
m_setter = setmethod;
}
};
template
class Property
{
protected:
typedef Type (Owner::*getter)();
Owner * m_owner;
getter m_getter;
public:
// Оператор приведения типа. Реализует геттер.
operator Type()
{
return (m_owner->*m_getter)();
}
Property() :
m_owner(NULL),
m_getter(NULL)
{
}
Property(Owner * const owner, getter getmethod) :
m_owner(owner),
m_getter(getmethod)
{
}
void init(Owner * const owner, getter getmethod)
{
m_owner = owner;
m_getter = getmethod;
}
};
template
class Property
{
protected:
typedef void (Owner::*setter)(Type);
Owner * m_owner;
setter m_setter;
public:
// Оператор присваивания. Реализует сеттер.
void operator =(Type data)
{
(m_owner->*m_setter)(data);
}
Property() :
m_owner(NULL),
m_setter(NULL)
{
}
Property(Owner * const owner, setter setmethod) :
m_owner(owner),
m_setter(setmethod)
{
}
void init(Owner * const owner, setter setmethod)
{
m_owner = owner;
m_setter = setmethod;
}
};
Using
You can use it like this:
TestClass.h file
#pragma once
#include "Property.h"
class TestClass
{
public:
TestClass(void);
~TestClass(void);
void _setterRW(int a);
int _getterRW();
Property testRW;
int _getterRO();
Property testRO;
void _setterWO(int a);
Property testWO;
private:
int propRW;
int propRO;
int propWO;
};
File TestClass.cpp
#include "TestClass.h"
TestClass::TestClass(void)
{
testRW.init(this, &TestClass::_getterRW, &TestClass::_setterRW);
testRO.init(this, &TestClass::_getterRO);
propRO = 123;
testWO.init(this, &TestClass::_setterWO);
}
int TestClass::_getterRW()
{
return propRW;
}
void TestClass::_setterRW(int a)
{
propRW = a;
}
int TestClass::_getterRO()
{
return propRO;
}
void TestClass::_setterWO(int a)
{
propWO = a;
}
TestClass::~TestClass(void)
{
}
File main.cpp
#include "TestClass.h"
#include "stdio.h"
int main()
{
TestClass t;
t.testRW = 15;
int a = t.testRW;
t.testWO = 34;
//a = t.testWO; //ошибка: чтение WriteOnly property
//t.testRO = 45; //ошибка: запись в ReadOnly property
printf("RW = %d\n", int(t.testRW));
printf("RO = %d\n", int(t.testRO));
scanf("%d", a);
return 0;
}
Conclusion
This implementation suits me, but I do not like it for several reasons. The first is the need to specify the class to which the property belongs. The second is the need to initialize each property in the constructor, that is, at the execution stage. The third is the impossibility of creating a static property. Well, the fourth, less significant: the tip shows the Property type, and not what we write there, which may cause some questions from the one who first sees it. Also, the topic of pointers to class methods is very muddy, poorly disclosed and of little use. It is well explained here , but the most important thing is that I realized that you need to use them very carefully.
It turned out not so intuitive to use as I wanted, but the goal was achieved.
Used materials:
www.rsdn.ru/article/vcpp/props.xml - the main ideas.
www.rsdn.ru/forum/cpp/854559.1.aspx - an idea with access rights.
rsdn.ru/article/cpp/fastdelegate.xml - a detailed explanation of pointers to functions and methods of classes in C ++.
Sources with examples here .
Visual Studio 2008 project here .
This text is published under the CC-BY License .