Properties

Home Page Narrow Gate Logic !>Jonatan*PL cattery<!  kurs "Filip"  Mail me

Home ] Up ] What's new ] Downloads ] Interests ] Education ] Projects ] Programming ] Arts ] Journeys ] Photos ] Jokes ] Friends ] Links ]

 

Description 

Solution

Implementation

New problem

Source

 

Property in std. C++

Description of problem

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. 

Solution

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.

Sample implementation

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;

};

 

New problem

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. 

Source

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 !

 

Home Page Top of page Up ] Next ]

Up ]

Page created 2001-03-17 17:02:36, last edited 2003-11-10 12:44:51   by Marcin Wudarczyk