Property in std. C++
In many object-oriented languages there are such things like properties
of an object. It's an another tool of encapsulation. For the user of your object,
a property looks almost like a variable member of the class. But from your
point of view (as a developer) a property looks like two methods: one
for getting value of the property and one for setting the value. You have
to implement them, and then, when user uses the property in an expression
other than assignment, the get method is called. If a user assigns
something to your property, then the set method is called.
Such languages like C++Builder, Delphi, Visual Basic and, for example,
COM, are using properties very widely. So it would be a great idea to make
it for programmers in C++ possible to use this mechanism.
All problems with object models I try to solve in a way that no
overhead of memory or run-time is imposed. And this is the case, too. One
way is to create a new nested class, in which we would store pointer to
the owner of the property. Then, it is possible to call a method of owner
class. There should be operator type() defined,
where type is the type of property, and which
would call the get method and return the value returned by the method. The
second thing is assignment operator, in which also would call a set method
of owner object with the parameter.
But this is stupid way, because it is needed to allocate four bytes for
every property, and all that memory will be wasted and the information
would be redundant.
So, if there is no pointer, where take the address of owner object
from? The answer is: from the this pointer! All we have to know is
what the offset of address of property in the owner object is. All we have
to do then is to subtract this offset from this pointer of property
object and convert it to the owner's type. Now we can call owner's methods.
class
owner {
public:
int propval;
int getprop() const { return propval; }
void setprop(int co) { propval = co; }
class prop {
#define
offset 4
public:
operator int() const { return (((owner*) ((char
*)this)-offset))->getprop(); }
prop
&operator=(int co) { (((owner*) ((char *)this)-offset))->setprop(co);
return *this; }
#undef
offset
}
val;
};
Now, all seems to be all right. All processing we do is in
compile-time, no overhead and no memory loss, all functions can be inlined,
and we have cool tool for modeling. Is that really so?
Well, the answer is no, unfortunately. The problem is, the compilers do
not allow a class to have zero-length. So, each property object will will
take at least one byte of spare memory. This problem is now solved in
C++Builder 5.0, which allows zero-length class members. But there are two
kinds of properties in C++Builder 5.0 already, so why would we would like
to do our own?
But in the older compilers the problem makes our solution not as pretty
as it seemed. Some of you may think: but the solution is obvious - all you
have to do is put all those properties into one anonymous union, with one
of regular members. But it won't work. The reason is simple -
compilers doesn't allow to have an object of a class with user-defined constructors
or assignment operators inside a union.
So there is only one way to solve the problem: put some of members of
owner class to the properties objects. The best idea is to put there
members that really store the value of property. You can still define your
own set method in the owner class.
Still, there is a problem with porting it t another systems, connected
to alignment of data. This can cause offsets to change without reason and
makes your code less portable. And still, the need to calculate and enter
the offsets is very annoying. Maybe it is now possible to get those
offsets from data class member pointers in compile-time? In BC 3.1 it is
not, in newer ones I haven't tried. If you do, let me know.
Here you have a header file created by me. It is described in polish,
but this article should help you understand the meaning of each class. I
will try to post a description of each class later.
The file consist of several macros staring with prop. The
parameters are mainly the same:
prop(T,off,C,name)
T means type of owner object, off is an number of bytes
from the beginning of owner object to the beginning of property object. C
is name of property type, and name is name of property.
In the previous example instead of class
prop definition, one could write:
prop(owner, 4, int, val);
There are several kinds of properties, denoted by suffix of macro.
- no suffix - normal property, as described in the article. The
names of get and set functions are as follows: name_get
and name_set, where name is name of property (VB
style).
- -n - it has altered names of get and set
functions: getname and setname (C/Pascal
style).
- -d - it has a one data member of type C (same
as the type of property), rest same as in no suffix version.
- -dn - combination of -n and -d.
- -dec - only definition of the property class, no
definitions of member functions. Use when you want to inline get and
set functions of owner class, but cannot define them in the body of
owner class. Use also if you want C/Pascal styled names.
- -ddec - both -dec and -d.
- -def - definition of members of property defined
with -dec macros family. Use both for properties declared with propdec
and propddec.
- -ndef - -n and -def combined.
- -dagetdef - automatically defines get function to
get the value from the member of property object. Use only for
properties declared with -d suffix.
- -dnagetdef - combination of -dagetdef and -n.
- -pref - introduces new parameter. The first now
is a prefix, which will be placed before each definition of member
function. For use with template owner classes. Currently only
implemented together with other prefixes: -dprefagetdef and -dnprefagetdef
are only recognized.
Note: this was to work under BC 3.1. It is possible that there is
abeeter way to implement it it newer compilers.
So, that's all for now, enjoy !
|