CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 15
  1. #1
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    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.

  2. #2
    Join Date
    Jan 2004
    Location
    Düsseldorf, Germany
    Posts
    2,401

    Re: template design question

    Quote 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.

  3. #3
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    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

  4. #4
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470

    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


  5. #5
    Join Date
    Jun 2002
    Location
    Moscow, Russia.
    Posts
    2,176

    Re: template design question

    Quote 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."

  6. #6
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    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.

  7. #7
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    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

  8. #8
    Join Date
    Jun 2002
    Location
    Moscow, Russia.
    Posts
    2,176

    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."

  9. #9
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    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

  10. #10
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    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?

  11. #11
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    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

  12. #12
    Join Date
    Jun 2002
    Location
    Moscow, Russia.
    Posts
    2,176

    Re: template design question

    Quote 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."

  13. #13
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    Re: template design question

    Quote 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.

  14. #14
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    Re: template design question

    Quote 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

  15. #15
    Join Date
    Nov 2006
    Location
    Essen, Germany
    Posts
    1,344

    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
  •  





Click Here to Expand Forum to Full Width

Featured