BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

How to store 'references' to properties in a class?

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Usage)
View previous topic :: View next topic  
Author Message
M_R
Guest





PostPosted: Fri May 27, 2005 9:59 am    Post subject: How to store 'references' to properties in a class? Reply with quote



I'm trying to stora a class which should hold either an int-value or a
pointer to an int-value. (Element 'type' contains information about
which of the both is saved here).

If I have a constant, I store the int-value, if it is a 'dynamic'
value, e.g. the changing vlaue of a variable v, I use the pointer
(i.e. &v).

Now I need to store a 'dynamic' property value, e.g. for a TBitmap
my_bitmap 'my_bitmap->Width'. Unfortunately a property has no adress.

So is there a way to expand my class that way I can initiate it with a
property somehow, so that a get_val-method() inside it would return
the actual value this property contains?

~~~~~~
The (reduced to the essential) class is this:

class MyArg {
public:
char type; // valid chars:'i' (int), 'I' (int *)
private:
union {
int value_int;
int *value_int_ptr;
};
public:
MyArg() {
init(0);
}
void init(int _val) {
type='i';
value_int=_val;
}
void init(int *_val_ptr) {
type='I';
value_int_ptr=_val_ptr;
}
get_value(void) {
return(type=='i' ? value_int : *value_int_ptr);
}
};

~~~~~~~~~~~~~~~

Thanks a lot,

Michael
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Fri May 27, 2005 5:52 pm    Post subject: Re: How to store 'references' to properties in a class? Reply with quote




<M_R> wrote


Quote:
Now I need to store a 'dynamic' property value, e.g. for a TBitmap
my_bitmap 'my_bitmap->Width'. Unfortunately a property has no adress.

Worse than that, a property may not be tied to a variable at all.
Properties can call methods to get/set their values.

Quote:
So is there a way to expand my class that way I can initiate it
with a property somehow, so that a get_val-method() inside it
would return the actual value this property contains?

There are a couple of ways to do that:

1) store the object pointer and use the VCL's RTTI system to access the
property dynamically. This only works for __published properties, though
(TBitmap::Width is not __published). For example:

#include <TypInfo.hpp>

enum MyArgType {matInt, matIntPtr, matObjProp};

class MyArg
{
private:
union
{
int value;
int* value_ptr;
struct {
TObject* obj;
PPropInfo prop;
};
};
public:
MyArgType type;
MyArg()
{
init(0);
}
void init(int _val)
{
type = matInt;
value = _val;
}
void init(int *_val_ptr)
{
type = matIntPtr;
value_ptr = _val_ptr;
}
void init(TObject *_obj, const AnsiString &PropName)
{
type = matObjProp;
obj = _obj;
prop = GetPropInfo(obj, PropName);
}
int get_value(void)
{
if( type == matInt )
return value;
else if( type == matIntPtr )
return *value_ptr;
else
return GetOrdProp(obj, prop);
}
};


2) a better approach is to write additional specialized classes that wrap
your values, and then you can have your main class delegate to the wrapper
classes via polymorphism. This way, your main class does not need to know
anything specific about the type of values it is working with. The wrapper
classes hide the details. This is a much more flexible design. For
example:

class MyArgValue
{
public:
MyArgValueBase() {}
virtual ~MyArgValue() {}
virtual int get_value() = 0;
};

class MyArgValueInt : public MyArgValue
{
private:
int value;
public:
MyArgValueInt(int _value) : MyArgValueBase(), value(_value) {}
virtual int get_value() { return value; }
};

class MyArgValueIntPtr : public MyArgValue
{
private:
int* value;
public:
MyArgValueIntPtr(int *_value) : MyArgValueBase(), value(_value) {}
virtual int get_value() { return *value; }
};

class MyArgValueBitmapWidth : public MyArgValue
{
private:
Graphic::TBitmap bmp;
public:
MyArgValueBitmapWidth(Graphics::TBitmap *_bmp) : MyArgValue(),
bmp(_bmp) {}
virtual int get_value() { return bmp->Width; }
};

class MyArg
{
private:
MyArgValue *value;
public:
MyArg(MyArgValue* _value = NULL) { init(_value); }
void init(MyArgValue* _value) { value = _val; }
int get_value(void)
{
if( value )
return value->get_value();
return 0;
}
};


Gambit



Back to top
M_R
Guest





PostPosted: Sat May 28, 2005 8:31 am    Post subject: Re: How to store 'references' to properties in a class? Reply with quote



Thanks, Gambit, for your very extensive answer.

I temporarily 'solved' my problem with defining the possibility to
give a pointer to a function which is called to evaluate the
int-value.

This works fine so far, but your solutions seem to be more elegant, so
I consider changing my design. I especially like your first version,
because as it seems there is no need to create a new class as in the
second approach (or looking at my design function) for each new
property, e.g. my_bitmap->Width, my_bitmap->Height, ...

Thanks a lot,

Michael
Back to top
M_R
Guest





PostPosted: Sun May 29, 2005 12:32 pm    Post subject: Re: How to store 'references' to properties in a class? Reply with quote

Being curious about the two ways, I tried both (and both worked fine),
I have some smaller questions about them:

I'm using BCB5

First I had to modify them a little, to make it run, so I copied the
modified classes (and a little test). The '???' indicates where I made
a necessary modification, hoping not mess things up...

Way 1)

#include <TypInfo.hpp>

enum MyArgType {matInt, matIntPtr, matObjProp};

class MyArg
{
private:
union
{
int value;
int* value_ptr;
struct {
TObject* obj;
PPropInfo prop;
};
};
public:
MyArgType type;
MyArg()
{
init(0);
}
void init(int _val)
{
type = matInt;
value = _val;
}
void init(int *_val_ptr)
{
type = matIntPtr;
value_ptr = _val_ptr;
}
void init(TObject *_obj, const AnsiString &PropName)
{
type = matObjProp;
obj = _obj;
prop = GetPropInfo(obj, PropName, TTypeKinds()/*???*/);
}
int get_value(void)
{
if( type == matInt )
return value;
else if( type == matIntPtr )
return *value_ptr;
else
return GetOrdProp(obj, prop);
}
};


~~~~~~~

bool Test_MyArg(void) {
bool success=true; // so far...
class MyArg my_arg;
my_arg.init(Application->MainForm, "Height");
success=(my_arg.get_value()==Application->MainForm->Height);
if(success) {
Application->MainForm->Height++;
success=(my_arg.get_value()==Application->MainForm->Height);
}
return(success);
}

~~~~~~~

Questions

a) What is the meaning of the TTypeKinds-argument in GetPropInfo?
(BCB5 needs it). I did not find any information in the help-system. I
would suppose it's the set of (from the user) allowed types. So the
function would e.g. return NULL, if the user allows only tkChar, but
the property is of type int!?? (Argument TTypeKinds() would mean:
accept any type...)

a2) Is the snipppet

else {
Typinfo::TTypeKind kind=prop->PropType[0]->Kind; // ???
assert(kind==tkInteger);
return GetOrdProp(obj, prop);
}

correct, which should test, if the property really is an
int-property?

[ I' m little irritated about prop->PropType is a pointer to pointer
to TTypeInfo(, instead of just a pointer to TTypeInfo). The reason
might be that prop could hold more than one TTypeInfo!?]

b) Istead of giving the ' void init(TObject *_obj, const AnsiString
&PropName)'-function the property-name, is it possible to give it some
kind of int-constant (calculated at compile-time)?

~~~~~~
~~~~~~

Way 2.

class MyArgValueBase/*???*/ {
public:
MyArgValueBase() {}
virtual ~MyArgValueBase() {}
virtual int get_value() = 0;
};

class MyArgValueInt : public MyArgValueBase
{
private:
int value;
public:
MyArgValueInt(int _value) : MyArgValueBase(), value(_value) {}
virtual int get_value() { return value; }
};

class MyArgValueIntPtr : public MyArgValueBase
{
private:
int* value;
public:
MyArgValueIntPtr(int *_value) : MyArgValueBase(), value(_value)
{}
virtual int get_value() { return *value; }
};

class MyArgValueBitmapWidth : public MyArgValueBase
{
private:
Graphics::TBitmap *bmp;
public:
MyArgValueBitmapWidth(Graphics::TBitmap *_bmp) :
MyArgValueBase(), bmp(_bmp) {}
virtual int get_value() { return bmp->Width; }
};

~~~~ Extention and Test-Function

class MyArgValueFormHeight : public MyArgValueBase
{
private:
TForm *form;
public:
MyArgValueFormHeight(TForm *_form) : MyArgValueBase(),
form(_form) {}
virtual int get_value() { return form->Height; }
};

bool Test_MyArgValue(void) {
bool success=true; // so far...
class MyArgValueFormHeight my_arg(Application->MainForm);
success=(my_arg.get_value()==Application->MainForm->Height);
if(success) {
Application->MainForm->Height++;
success=(my_arg.get_value()==Application->MainForm->Height);
}
return(success);
}


Question:

1) I suppose the base-class should be MyArgValueBase (instead of class
MyArgValue)...

2) Yes, I agree, it's more elegant. But - as said - what I didn't like
that much is the need for having to create an additional class for
each property used. Isn't it possible this to be done automatically,
e.g. using 'template<..>' somehow?


Thanks a lot,

Michael


Back to top
M_R
Guest





PostPosted: Mon May 30, 2005 6:44 am    Post subject: Re: How to store 'references' to properties in a class? Reply with quote

Sorry: when implementing 'way 1' this for TBitmap a problem arises:

Analysing it , I found out, the reason was, that

TPropInfo *property_info=GetPropInfo(image->Picture->Bitmap, "Height",
TTypeKinds() << tkInteger/*???*/);

(
and
PropInfo *property_info=GetPropInfo(image->Picture->Bitmap, "Height",
TTypeKinds());
too
)

returned NULL.

I tried it with an Image intitalized with a visible picture in the
designer Image1 too: same result...

I suppose the reason might be that TBitmap's Height is not published!?

So is there an alternative for GetPropInfo()? If not, how can the
Object-Inspector used when debugging this line can find and display
it?

~~~~~~~~~~~~

When copying and editing 'way2' I forgot the MyArg-class, so here it
is. But now I do not know how to initialize the constructor (or the
init), since - as I learned now - a property does not have an adress.
I marked the place as before with a ???

class MyArg2 // renaming it to avoid conflict with MyArg from 'Way
1'
{
private:
MyArgValueBase *value;
public:
MyArg2(MyArgValueBase* _value = NULL) { init(_value); }
void init(MyArgValueBase* _value) { value = _value; }
int get_value(void)
{
if( value )
return value->get_value();
return 0;
}
};

bool Test_MyArgValue2(void) {
bool success=true; // so far...
class MyArg2 my_arg(/*???*/); // how to initialize it correctly?
success=(my_arg.get_value()==Application->MainForm->Height);
if(success) {
Application->MainForm->Height++;
success=(my_arg.get_value()==Application->MainForm->Height);
}
return(success);
}

Thanks a lot in advance,

Michael
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Tue May 31, 2005 1:36 am    Post subject: Re: How to store 'references' to properties in a class? Reply with quote


<M_R> wrote


Quote:
I especially like your first version, because as it seems there is no
need to create a new class as in the second approach

As I mentioned in my previous message, the first approach is not always
usable. It only works for VCL objects with properties that are declared as
__published, since only __published properties are accessable via the RTTI
system. You stated a specific case where you needed to access a
TBitmap::Width property. Although TBitmap is a VCL object, it does not have
any __published properties, hense the first approach would not work.

As for the second case, it does not rely on VCL-specific features at all, so
it is much more flexible. You can write new classes for new situations that
plug right into the code without having to change any of the other code at
all.


Gambit



Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Tue May 31, 2005 1:56 am    Post subject: Re: How to store 'references' to properties in a class? Reply with quote


<M_R> wrote


Quote:
prop = GetPropInfo(obj, PropName, TTypeKinds()/*???*/);

This would be better:

prop = GetPropInfo(obj, PropName, TTypeKinds() << tkInteger);

Alternatively, GetPropInfo() has other overloaded versions that do not take
a TTypeKinds parameter, ie:

prop = GetPropInfo(static_cast
Quote:
a) What is the meaning of the TTypeKinds-argument in GetPropInfo?

It allows GetPropInfo() to fine-tune which properties it returns by
comparing the actual data type for the property. If you don't specify any
type, then GetPropInfo() returns the first property that matches the
spcified Name without regard to the data type. In this particular
situation, you want to work with integer-based properties only, so you can
tell GetPropInfo() to only return a property of the specified Name if it
also matches the specified data type as well.

Quote:
(BCB5 needs it). I did not find any information in the help-system.

The RTTI system is not documented by Borland at all.

Quote:
I would suppose it's the set of (from the user) allowed types. So
the function would e.g. return NULL, if the user allows only tkChar,
but the property is of type int!??

Something like that, yes.

Quote:
a2) Is the snipppet

else {
Typinfo::TTypeKind kind=prop->PropType[0]->Kind; // ???
assert(kind==tkInteger);
return GetOrdProp(obj, prop);
}

correct, which should test, if the property really is an
int-property?

If you specify tkInteger when you call GetPropInfo() then you are guaranteed
to have a valid PPropInfo pointer that will work with GetOrdProp(), as long
as GetPropInfo() returns a non-NULL pointer in the first place.

If you must test the property type manually, then you should use the
PropIsType() function instead, ie:

assert(PropIsType(obj, PropName, tkInteger));

Quote:
b) Istead of giving the ' void init(TObject *_obj, const AnsiString
&PropName)'-function the property-name, is it possible to give it
some kind of int-constant (calculated at compile-time)?

For what purpose exactly? Please be more specific. Properties are always
referenced by name when having the RTTI system retreive the associated
PPropInfo pointer.

Quote:
class MyArgValueBase/*???*/ {

When I first wrote the code, I named it 'MyArgValueBase' but then later
removed the 'Base' portion before posting it online. I guess I forgot to
change all of the references. I meant for the final code to use
'MyArgValue' instead.

Quote:
2) Yes, I agree, it's more elegant. But - as said - what I didn't
like that much is the need for having to create an additional class
for each property used.

That depends on your actual needs. The code can always be generalized more
so that the classes are reusable for multiple situations.

Quote:
Isn't it possible this to be done automatically, e.g. using
'template<..>' somehow?

For VCL objects, a template generally would not work in this situation
because you cannot specify property names via template parameters, unless
you use GetPropInfo() again. Also, the trick about templates is that
although you would not be writing separate classes yourself in code, for
each different parameter type/value you specify in code, the compiler still
creates new classes anyway. So you wouldn't be gaining any run-time
performance by using a template anyway.

The alternative would be to use precompiler macros to generate class names
and property names based on token merging via the ## operator. But again,
you would still end up with separate classes during compile-time for each
set of parameters you specify in code.


Gambit



Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Tue May 31, 2005 2:06 am    Post subject: Re: How to store 'references' to properties in a class? Reply with quote


<M_R> wrote


Quote:
Sorry: when implementing 'way 1' this for TBitmap a problem arises:

I already told you several times now that TBitmap does not have any
__published properties at all, so you cannot use the RTTI approach for
accessing its properties. That is specifically why I wrote the second
approach, which does not rely on RTTI at all.

Quote:
TPropInfo *property_info=GetPropInfo(image->Picture->Bitmap,
"Height", TTypeKinds() << tkInteger/*???*/);
snip
returned NULL.

As well it should, because TBitmap doesn't have any RTTI-accessible
properties at all.

Quote:
I suppose the reason might be that TBitmap's Height is not published!?

Correct.

Quote:
So is there an alternative for GetPropInfo()?

I already gave you the alternative - the entire second set of code that is
based on polymorphic classes.

Quote:
If not, how can the Object-Inspector used when debugging this
line can find and display it?

The Debugger doesn't use the RTTI system at all. The Debugger knows the
exact memory layout of every object that the compiler created, so it can
directly display the memory contents in a structured layout.

Quote:
When copying and editing 'way2' I forgot the MyArg-class, so here
it is. But now I do not know how to initialize the constructor (or the
init), since - as I learned now - a property does not have an adress.
I marked the place as before with a ???

You really don't understand polymorphism yet, do you?

The whole purpose of that code was for there to be an abstract base class
that other classes derive from. Then you write specialized classes for each
situation that you want to support, that the main class does not have to
have any concept whatsoever of where the data values are actually coming
from. All the main class cares about is the abstract base class. Then you
can pass in any derived class to it that you want.

class MyArg2
{
private:
MyArgValueBase *value;
public:
MyArg2(MyArgValueBase* _value = NULL) { init(_value); }
void init(MyArgValueBase* _value) { value = _value; }
int get_value(void)
{
if( value )
return value->get_value();
return 0;
}
};

bool Test_MyArgValue2(void)
{
MyArgValueFormHeight arg_value(Application->MainForm);
MyArg2 my_arg(&arg_value);
bool success = ( my_arg.get_value() ==
Application->MainForm->Height );
if( success )
{
Application->MainForm->Height = (Application->MainForm->Height +
1);
success = (my_arg.get_value() == Application->MainForm->Height);
}
return success ;
}

I stronly suggest you learn how to effectively use polymorphism. It is the
foundation of Object Oriented Programming.


Gambit



Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Usage) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.