|
-
November 20th, 2006, 11:34 AM
#1
template design question
Hi,
I´m messing around with the borland VCL and have to accomplish the following task: There are many types (like TStaticText, TLabel, TEdit ...) that have properties with the same name, but unfortunately there´s no common base class with these properties made public, so I can´t use
Code:
((TWinControl *) pObject)->Text = "Hello World";
Example:
Code:
class TWinControl
{
protected:
AnsiString Caption;
...
};
class TLabel : public TWinControl
{
public :
AnsiString Caption;
...
};
class TStaticText : public TWinControl
{
public:
AnsiString Caption;
};
I read component properties (text color, font style, etc.) from a file and have to assign these properties to different control types. My current naive approach is providing a function for each control type and perform customization inside that function:
Code:
void CustomizeLabel( TLabel *pLabel, TComponentProperties *pProps )
{
pLabel->Text = pProps->Text;
pLabel->Color = pProps->Color;
...
}
void CustomizeMenu( TMenu *pMenu, TComponentProperties *pProps )
{
...
}
void CustomizeButton( TButton *pButton, TComponentProperties *pProps )
{
...
}
My second approach is to use templates and traits to handle different control types:
Code:
template<class TTarget> TNoOp
{
TNoOp( TObject *pTarget, TComponentProperties *pProps )
{
}
}
template<class TTarget> TCaptionSetter
{
TCaptionSetter( TObject *pTarget, TComponentProperties *pProps )
{
((TTarget *) pTarget)->Caption = pProps->Text;
}
}
template<class TTarget> TColorSetter
{
TColorSetter( TObject *pTarget, TComponentProperties *pProps )
{
((TTarget *) pTarget)->Color = pProps->Color;
}
template<class TTarget, class TTextHandler, class TColorHandler> class TCustomizer
{
TCustomizer( TObject *pTarget, TComponentProperties *pProps )
{
TTextHandler( pTarget, pProps );
TColorHandler( pTarget, pProps );
}
}
Now I can use the following constructs to assign text and/or color:
Code:
// set caption and color
TCustomizer<TLabel, TCaptionSetter<TLabel>, TColorSetter<TLabel> Cust( pObj, pProps );
// set caption only, ignore color
TCustomizer<TMenu, TCaptionSetter<TMenu>, TNoOp<TMenu> Cust( pObj, pProps );
Using the code above I have to specify the type name for each of the traits, making the code less readable. The type names are redundant, because they´re already known by the class TCustomizer, so I want to get rid of the template spec for the traits. I´m quite new to templates and generic programming, is it possible to create a construct without specialized trait classes? Like
Code:
template<class TTarget, class TCaptionSetter, class TColorSetter> TCustomizer
{
TCustomizer( TObject *pObj, TComponentProperties *pProps )
{
TCaptionSetter ts( pObj, pProps );
TColorSetter cs( pObj, pProps );
}
}
I want to use the template this way:
Code:
TCustomizer<TLabel, TCaptionSetter, TNoOp> Cust( pObj, pProps );
Third approach (just coming in mind, I don´t know if it will work):
Code:
template<class TTarget, class TTextHandler, class TColorHandler> class TCustomizer
{
TTarget *m_pTarget;
TComponentProperties *m_pProps;
class TColorSetter( TCustomizer<TTarget> *pCustom )
{
pCustom->Target->Color = pCustom->m_Props->Color;
}
class TCaptionSetter( TCustomizer<TTarget> *pCusom )
{
pCustom->Target->Caption = pCustom->m_Props->Text;
}
TCustomizer( TObject *pTarget, TComponentProperties *pProps )
{
m_pTarget = (TTarget *) pTarget;
m_pProps = pProps;
TTextHandler th( this );
TColorHandler tc( this );
}
}
Can I use
Code:
TCustomizer<TLabel, TCustomizer::TTextSetter,TCustomizer::TColorSetter> Cust( pObj, pProps );
?
Edit: Third approach can´t work, please ignore.
Thanks in advance,
Guido
Last edited by GNiewerth; November 21st, 2006 at 03:34 AM.
-
November 20th, 2006, 02:42 PM
#2
Re: template design question
 Originally Posted by GNiewerth
My current naive approach is providing a function for each control type and perform customization inside that function:
Code:
void CustomizeLabel( TLabel *pLabel, TComponentProperties *pProps )
{
pLabel->Text = pProps->Text;
pLabel->Color = pProps->Color;
...
}
void CustomizeMenu( TMenu *pMenu, TComponentProperties *pProps )
{
...
}
void CustomizeButton( TButton *pButton, TComponentProperties *pProps )
{
...
}
Why not simply make a templated function out of the above:
Code:
template <typename T>
void Customize( T& pWidget, TComponentProperties& pProps )
{
pWidget.Text = pProps.Text;
pWidget.Color = pProps.Color;
...
}
This should allow you to call the function for any "compatible" Widget. E.g.
Code:
TLabel label;
TButton button;
TComponentProperties prop;
Customize(label, prop);
Customize(button, prop);
Hope this helps.
-
November 21st, 2006, 03:04 AM
#3
Re: template design question
Hi Treuss,
some of the components have properties that others don´t have. Some can display balloon tips while others have a color property. Overall there are only 4 different properties, but that make 24 different template functions. In addition to that, some controls use different properties to set their text, in some cases it´s Caption, in others it´s Text.
I can´t use WM_SETTEXT / WM_SETFONT either because some of the components are lighweight and don´t support windows messages.
Most of the controls have technically nothing in common, that´s why I want to use templates
Regards,
Guido
-
November 21st, 2006, 04:36 AM
#4
Re: template design question
Traits classes and adapter functions might help here:
Code:
template <class Widget>
void SetText(Widget& w, const std::string& text)
{
w.SetText(text);
}
template <>
void SetText<AwkwardWidget>(AwkwardWidget& w, const std::string& text)
{
w.SetCaption(text);
}
template <class Widget>
void DoSomething(Widget& w)
{
// ...
SetText(w, text_string);
// ...
}
Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
-- Sutter and Alexandrescu, C++ Coding Standards
Programs must be written for people to read, and only incidentally for machines to execute.
-- Harold Abelson and Gerald Jay Sussman
The cheapest, fastest and most reliable components of a computer system are those that aren't there.
-- Gordon Bell
-
November 21st, 2006, 05:37 AM
#5
Re: template design question
 Originally Posted by GNiewerth
Hi Treuss,
some of the components have properties that others don´t have. Some can display balloon tips while others have a color property. Overall there are only 4 different properties, but that make 24 different template functions. In addition to that, some controls use different properties to set their text, in some cases it´s Caption, in others it´s Text.
I can´t use WM_SETTEXT / WM_SETFONT either because some of the components are lighweight and don´t support windows messages.
Most of the controls have technically nothing in common, that´s why I want to use templates
Regards,
Guido
Who owns the knowledge about identities of fields in property classes you need to get? About corresponding widget properties you need to set?
That is, can't these methods be just methods of widget taking property collection object and extracting proper information, with client (caller of these methods) unaware of both class of widget, class of property collection and list of properties to be copied?
Code:
class TWidget {
public:
virtual void Costomize(const TComponentProperties& props);
}
class TLabel : public TWidget {...}
void TLabel::Customize(const TComponentProperties &props )
{
Text = props.Text;
Color = props.Color;
...
}
Last edited by RoboTact; November 21st, 2006 at 05:41 AM.
"Programs must be written for people to read, and only incidentally for machines to execute."
-
November 21st, 2006, 05:40 AM
#6
Re: template design question
Thanks Graham,
The problem is a little bit more complex. I´ll give the full approach of my first solution:
Code:
typedef void (*pfnCallback)( TObject *, TComponentProperties *);
typedef std::map<AnsiString, pfnCallback> CallbackMap;
void CustomizeComponent( TObject *pObj, TComponentProperties *pProperties )
{
CallbackMap::iterator it;
// find callback for Class
it = CallbackMap.find( pObj->ClassName() );
if( it != CallbackMap.end() )
{
// call callback
it->second( pObj, pProperties );
}
}
void CustomizeLabel( TObject *pObj, TComponentProperties *pProps )
{
// set text
((TLabel *) pObj)->Caption = pProps->Text;
// set bk color
((TLabel *) pObj)->Color = pProps->Color;
// set balloon tip
((TLabel *) pObj)->Hint = pProps->Hint;
// set font
((TLabel *) pObj)->Font = pProps->Font;
}
void CustomizeEdit( TObject *pObj, TComponentProperties *pProps )
{
// set text
((TEdit *) pObj)->Text = pProps->Text;
// set font
((TEdit *) pObj)->Font = pProps->Font;
}
void CustomizeMenuItem( TObject *pObj, TComponentProperties *pProps )
{
// set text
((TMenuItem *) pObj)->Caption = pProps;
}
That´s a lot of redundant code because most of the components use the Caption property to show their text, so I have to call ((...*) pObj)->Caption = Text many times. If there´s a change to the Text property of my TComponentProperties I have to modify many lines of code (each time I assign this to a caption).
I have to make two decisions:
1) which properties do I have to set? (control dependent)
2) what are the property names I have to set? (control dependent)
Using your solution I can reduce the number of functions, but I still need about 15-20 functions to set every combination of properties to be set and property names.
Code:
template<TTarget> void SetText( ... ); // set text only
template<TTarget> void SetTextByCaption( ... ); // set text only using Caption
template<TTarget> void SetTextAndFont( ... ); // set text and font
template<TTarget> void SetTextByCaptionAndHint( ... ); // set text by caption and balloon tips
template<TTarget> void SetFontAndColor( ... ); // set font and color
My considerations were to make a class for each property type that performs the appropriate task:
class TCaptionSetter -> sets the caption of a control using the Caption property
class TTextSetter -> sets the caption of a control using the Text property
class TFontSetter -> sets the font of a control
class TColorSetter -> sets the font of a control
class THintSetter -> sets the hint of a control
class TNoOp -> does nothing
Now I want to construct the following template. I´ve been thinking over it for 1.5 days now and I´m not sure if it´s possible at all.
Code:
template<class TTarget, class TTextCmd, class TFontCmd, class TColorCmd, class THintCmd> class TCustomizer;
When customizing a TLabel and a TMenuItem object I want to use the template above in the following manner:
Code:
// Callback
void CustomizeLabel( TObject *pObj, TComponentProperties *pProps )
{
// customize all properties
TCustomizer<TLabel, TCaptionSetter, TFontSetter, TColorSetter, THintSetter> tc( pObj, pProps );
}
void CustomizeMenuItem(TObject *pObj, TComponentProperties *pProps )
{
// customize caption only
TCustomizer<TMenuItem, TCaptionSetter, TNoOp, TNoOP, TNoOp> tc( pObj, pProps );
}
Now redundant code is eliminated and it´s easy to extend to new control types with different property names (by adding a new TXYSetter class).
But:
At the moment I don´t know how to tell the setter classes what type of control they´re handling. When you look at my 2nd approach you´ll see that I have to declare the setter classes as templates as well:
Code:
template<class TTarget> TSetXY...
That will produce the following call for a TLabel:
Code:
TCustomizer<TLabel, TCaptionSetter<TLabel>, TFontSetter<TLabel>, TColorSetter<TLabel>, THintSetter<TLabel> > tc( pObj, pProps );
I want to remove the template specifications from my setter classes, I tried nested classes to share the information with the TCustomizer class:
Code:
template <TTarget, TTextCmd> TCustomizer
{
TTarget *m_pTarget;
TComponentProperties *m_pProperties;
class TTextSetter
{
TTextSetter( TCustomizer *pCustomizer )
{
pCustomizer->m_pTarget->Text = pCustomizer->m_pProperties->Text;
};
};
class TCaptionSetter
{
TCaptionSetter( TCustomizer *pCustomizer )
{
pCustomizer->m_pTarget->Caption = pCustomizer->m_pProperties->Text;
};
};
TCustomizer( TObject *pObj, TComponentProperties *pProps )
{
m_pTarget = (TTarget *) pObj;
m_pProperties = pProps;
// use given template class to set caption/text
TTextCmd( this );
};
}
I don´t know if it´s even possible to use this template because the qualification of the nested classes is infinitely recursive:
Code:
TCustomizer<TLabel, TCustomizer<TLabel, ?>::TTextSetter> tc( pObj, pProperties );
The ? has to be expanded to the fully qualified name of the TTextSetter class which in return needs a fully qualified TCustomizer<...,...> template which needs a fully qualified name of the TTextSetter again...
Now I got rid of the template specification for my setter classes but I can´t create an instance of my Customizer because I can´t name it...
Regards again,
Guido
PS:
I don´t think it´s possible to get rid of the template specification for the setter classes.
Last edited by GNiewerth; November 21st, 2006 at 06:02 AM.
-
November 21st, 2006, 05:44 AM
#7
Re: template design question
Unfortunately these components come from a library, so we can´t modify the source code. I can derive from these classes to use my own classes but then I have to edit 100+ forms and substitute the old class names by my new class names.
Regards,
Guido
-
November 21st, 2006, 06:10 AM
#8
Re: template design question
I see. Controls are external and you want to shorten function specifications by devising simple DSL.
You may want to check out Modern C++ Design book - there're many tricks like this you can do with templates (here you need typelists).
I'd just create collection of short-named helper functions operating in context where properties and widget are known and specify their sequence.
I wrote simple prototype:
Code:
#include <iostream>
using namespace std;
struct Wid
{
int x;
int caption;
};
struct Prop
{
int title;
int d;
};
template<typename W>
struct Setter
{
W& w;
const Prop& p;
Setter(W& w_, const Prop& p_) : w(w_), p(p_) {}
void title2caption() {w.caption = p.title;}
void d2x() {w.x = p.d;}
};
template<typename W>
void Customize(W& w, const Prop &p)
{
CustomizeImpl(Setter<W>(w,p));
}
/* many of these - for each widget Wid */
void CustomizeImpl(Setter<Wid> &s)
{
s.title2caption();
s.d2x();
}
int main()
{
Wid wid;
Prop prop;
prop.d = 11;
prop.title = 15;
Customize(wid, prop);
cout << wid.caption << " " << wid.x << endl;
return 0;
}
"Programs must be written for people to read, and only incidentally for machines to execute."
-
November 21st, 2006, 06:18 AM
#9
Re: template design question
Hi Robo,
there the circle closes... after reading some chapters of that book I decided to uses templates... and now I got problems I didn´t have before, and to solve these problems I have to read the book that caused the problems 
Wow, that´s real fun 
Thanks for your advice,
Guido
-
November 21st, 2006, 09:07 AM
#10
Re: template design question
Usually with Forms - there exists a Control Collection - atleast that is the case with .Net/VB (Controls? collection?)
Check if there is any for your case. I really don't hope there is because there's isn't a common base!
Before going forward though, how is this going to help you? Even if they will have the same property names but the values would be different. Moreover, setting caption etc are things that happen once when a form loads.. I think it isn't worth the pain. Can you negate this?
Can you help me with my homework assignment?, Before you post!, Use code tags, How to post!, Codeguru technical FAQs, C++ FAQ Lite, Stroustrup: C++ Style and Technique FAQ, Guru of the Week, Comeau C and C++ FAQs, Comeau C++ Templates FAQs, CUJ @ DDJ, Spam threshold
My Blogs : Learning C++ is fun | Abnegator's reflections
Open Threads : C++ Aha! Moments | Nature of work in C++?
-
November 21st, 2006, 09:33 AM
#11
Re: template design question
Hello Exterminator,
there´s a Borland class library called VCL that contains a number of different controls. Some are Win32 controls and some are Borland-specific lightweight controls. The most common class is TControl, but the significant members are protected, so I can´t access them at TControl level. Later the hierachy tree splits into TGraphicControl-derived classes (these are the Borland lightweight) and TWinControl-derived classes (Win32 classes). Still the significant members are protected. I can set most of the properties by sending specific window messages to the TWinControl derived classes, but it does not work for TGraphicControl-derived classes. In the beginning I could not believe it either, but it´s truly fact that most members that concern gui properties are made public in the topmost classes of the hierachy.
Now for the reason:
We´ve got a software pack that is installed and used all over the world. It´s primary languages are german and english, but often it´s used by industrial workers who don´t speak german nor english. Now I made a tool that reads a configuration file and assigns custom text/color/whatever to the appropriate control (much like a string table, except for more properties). Now a chinese can load the english configuration file, translate text into chinese and save that file. Later he configures the main software to load the chinese configuration file and the captions etc. are labeled in chinese (different codepages and character sets are already accounted)
Does that count as a negation? Don´t take it personal, but I don´t like people who are questioning other people´s problems without knowing the background.
Regards,
Guido
- Guido
-
November 21st, 2006, 10:20 AM
#12
Re: template design question
 Originally Posted by GNiewerth
Hello Exterminator,
there´s a Borland class library called VCL that contains a number of different controls. Some are Win32 controls and some are Borland-specific lightweight controls. The most common class is TControl, but the significant members are protected, so I can´t access them at TControl level. Later the hierachy tree splits into TGraphicControl-derived classes (these are the Borland lightweight) and TWinControl-derived classes (Win32 classes). Still the significant members are protected. I can set most of the properties by sending specific window messages to the TWinControl derived classes, but it does not work for TGraphicControl-derived classes. In the beginning I could not believe it either, but it´s truly fact that most members that concern gui properties are made public in the topmost classes of the hierachy.
So what you need is adapter for controls, not many-property-setting-routines. Use templates to generate adapters for common properties.
"Programs must be written for people to read, and only incidentally for machines to execute."
-
November 21st, 2006, 10:32 AM
#13
Re: template design question
 Originally Posted by GNiewerth
Does that count as a negation? Don´t take it personal, but I don´t like people who are questioning other people´s problems without knowing the background.
Yes, it does but sometimes for me it becomes necessary to understand the origin of the problem to predict the solution. And questioning without knowing background for background is okay isn't it? That is what I tried to ask!
Regarding your problem. There can be multiple approaches. I found that all the objects are derived from a base Object (or TObject) class. First thing you would need to do is maintain a collection of original text and then the translation if any:
Code:
//might use any other string type to accomodate wide chars for translations
struct CaptionStruct{
std::string originalText;
std::string translationText;
};
std::vector<CaptionStruct> CaptionsCollection;
This is something that you will keep all the time (or may be take some other decision on the lifetime depending upon how and where you use/need it). It is populated from your configuration file.
As I said earlier and you have not confirmed, that there may be a Controls collection associated with each form. If you have that, by the GUI library, well and fine else you would need to duplicate it. How would you duplicate it?
Code:
std::vector<TObject*> Controls;
//populate it on form load for each form individually.
How would you be able to what Control which one is - using ClassNameIs() method exposed from TControl. Now, depending upon what each would return as a class name - you will use the corresponding property to set the caption/text/tag/ whatever from the CaptionsCollection created above (you may need to use casting). Otherway would be:
Code:
std::map<TObject*, std::string> ControlsPropertyMap;
//OR
struct ControlProperty{
TObject * control;
std::string PropertyName;
};
std::vector<ControlProperty> ControlsPropertiesCollection;
Here the string value for the map would be a string telling the property to use, so if the PropertyName you filled is Caption - you cast the object and set the Caption property and so on what-so-ever it is looping over the ControlsPropertyMap/ControlsPropertiesCollection/Controls collection after searching for the text to be replaced from the original one from CaptionsCollection.
You cannot avoid casting. That is for sure as far as can think of right now. You cannot use templates effectively even because they are compile time solutions but you need to do all this at runtime depending upon what TObject is actually pointing to.. depending upon what is there in the Controls Collection for each form.
Also, note that all this translationing happens after the forms are loaded but before the become visible - something like in the Formload even or Activate event (whatever is their counterpart in BCL, I don't know). What I mean is, the original text should always get set and then you do a replace from the translation mapping. Similarly, you work with the properties related to color etc.
There may be better ways to do this.. but this is what I can think of. Good luck.
Last edited by exterminator; November 21st, 2006 at 10:36 AM.
Can you help me with my homework assignment?, Before you post!, Use code tags, How to post!, Codeguru technical FAQs, C++ FAQ Lite, Stroustrup: C++ Style and Technique FAQ, Guru of the Week, Comeau C and C++ FAQs, Comeau C++ Templates FAQs, CUJ @ DDJ, Spam threshold
My Blogs : Learning C++ is fun | Abnegator's reflections
Open Threads : C++ Aha! Moments | Nature of work in C++?
-
November 21st, 2006, 10:34 AM
#14
Re: template design question
 Originally Posted by RoboTact
So what you need is adapter for controls, not many-property-setting-routines. Use templates to generate adapters for common properties.
As I said before, I´m new to template and generic programming... can you give an example please?
Thanks in advance,
Guido
- Guido
-
November 21st, 2006, 11:58 AM
#15
Re: template design question
@Exterminator
Thanks for your reply. Yes, each form has a control collection which can be iterated over their names in a similar way reflections work (at least one nice Borland feature). Our team decided against it, instead we use hard coded IDs for each control. That guarantees that modifications of a form do not result in identy changes of a control. One would not be able to fix a typo from TTxName to TxtName when using reflection, which can be done when using hard coded IDs which are assigned manually.
After all I´ll stick to my second approach, I have tested it successfully.
Thanks to all of you,
Guido
Last edited by GNiewerth; November 21st, 2006 at 12:01 PM.
- Guido
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|