Problem: how to use one std container to store multiple types?

Discussion in 'C++' started by Manuel, Dec 27, 2005.

  1. Manuel

    Manuel Guest

    Hi.
    Before all, please excuse me for bad english and for very newbie
    questions. I hope to don't boring you.

    I'm trying to write a very simple GUI using openGL.
    So I'm writing some different widgets classes, like buttons, images,
    slider, etc...

    Each class has a draw() method.

    The idea is to put all widget to draw in a vector, and after, in pseudocode:

    -----------------------------
    for each widget w in vector:
    w.draw()
    -----------------------------

    So I should declare the vector, like

    vector<widget> widgetList;

    and this is my trouble: I've different classes (that has some common
    methods, like draw(), on(), off() etc...) and I should put all in one
    std container. It's possible?


    Really I don't know even how to declare the vector. In example, if I've
    the class

    ----------------------------
    class foo {
    private:
    int width;
    int height;
    public:
    int getWidth();
    int getHeight();
    };
    ----------------------------

    how to declare a container for foo? Surely not writing

    vector<foo> fooList;

    Should I use typedef? Can you explain me this?

    I know I'm a newbie and maybe these are very silly questions. However I
    can't find these info on the books I'm reading (they are for beginners,
    and don't write about STD) and the same using google.
    Can you help me?

    Thanks,


    Manuel
    Manuel, Dec 27, 2005
    #1
    1. Advertising

  2. Manuel wrote:
    >
    > I'm trying to write a very simple GUI using openGL. So I'm writing
    > some different widgets classes, like buttons, images, slider, etc...
    >
    > Each class has a draw() method.
    >
    > The idea is to put all widget to draw in a vector, and after, in
    > pseudocode:
    >
    > ----------------------------- for each widget w in vector: w.draw()
    > -----------------------------
    >


    Look at Design Pattern called Composite which can be used to wrap such
    structure easily. GoF's book [1] includes case study very similar to
    your problem so you may find this book intersting.

    Simply, create abstract base class i.e. Widget and
    inherit concrete (specialized) gadgets from the Widget.
    Declare in Widget pure virtual method i.e. void draw(canvas)
    and implement it in every concrete widget suitably.
    Finally, store all widgets in a container through pointer to the base
    class Widget (dynamic polymorphism).


    > So I should declare the vector, like
    >
    > vector<widget> widgetList;
    >
    > and this is my trouble: I've different classes (that has some common
    > methods, like draw(), on(), off() etc...) and I should put all in
    > one std container. It's possible?
    >


    Yes, do it as I explained above.
    Here is simple implementation which should help to understand that idea:

    #include <algorithm>
    #include <iostream>
    #include <vector>
    ////////////////////////////////////////////////////////////
    struct DeleteObject
    {
    template <typename T>
    void operator()(const T* ptr) const
    {
    delete ptr;
    }
    };
    ////////////////////////////////////////////////////////////
    class Widget
    {
    public:
    virtual void draw() const = 0;
    virtual ~Widget() {}
    };
    class Button : public Widget
    {
    public:
    void draw() const { std::cout << "Drawing Button" << std::endl; }
    };
    class Label : public Widget
    {
    public:
    void draw() const { std::cout << "Drawing Label" << std::endl; }
    };
    class Slider : public Widget
    {
    public:
    void draw() const { std::cout << "Drawing Slider" << std::endl; }
    };

    int main()
    {
    std::vector<Widget*> v;
    v.push_back(new Button());
    v.push_back(new Label());
    v.push_back(new Slider());
    v.push_back(new Button());

    std::vector<Widget*>::const_iterator it;
    for (it = v.begin(); it != v.end(); ++it)
    {
    (*it)->draw();
    }

    // Delete widgets
    std::for_each(v.begin(), v.end(), DeleteObject());

    return 0;
    }
    ////////////////////////////////////////////////////////////

    [1] Design Patterns: Elements of Reusable Object-Oriented Software
    by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides

    Cheers
    --
    Mateusz Åoskot
    http://mateusz.loskot.net
    =?UTF-8?B?TWF0ZXVzeiDFgW9za290?=, Dec 27, 2005
    #2
    1. Advertising

  3. Manuel

    Manuel Guest

    Mateusz Åoskot wrote:

    > Look at Design Pattern called Composite which can be used to wrap such
    > structure easily. GoF's book [1] includes case study very similar to
    > your problem so you may find this book intersting.
    >
    > Simply, create abstract base class i.e. Widget and
    > inherit concrete (specialized) gadgets from the Widget.
    > Declare in Widget pure virtual method i.e. void draw(canvas)
    > and implement it in every concrete widget suitably.
    > Finally, store all widgets in a container through pointer to the base
    > class Widget (dynamic polymorphism).


    Thanks you very much for the example!!

    Some questions:

    1) What happen if I use v.clear() instead DeleteObject() ??
    2) In your example each class has the same number of methods. But what
    happen if some classes have different numbers of methods? Should be not
    a problem, right ??
    3) Exist other way instead using a pointer in std::vector<Widget*> v; ??

    Again, thanks, THANKS!

    Ciao,

    Manuel
    Manuel, Dec 27, 2005
    #3
  4. Manuel wrote:
    >
    > Thanks you very much for the example!!
    >


    You're welcome!

    > Some questions:
    >
    > 1) What happen if I use v.clear() instead DeleteObject() ??


    Look at older thread titled "STL map and memory management (clear() )",
    about Dec, 15.

    > 2) In your example each class has the same number of methods. But
    > what happen if some classes have different numbers of methods? Should
    > be not a problem, right ??


    No problem. But remember that only common members may be accessed during
    iterations (without casting). I mean, when iterating you "can see" all
    widgets "through" base class interface - Widget.
    Just a figurative explanation :)

    > 3) Exist other way instead using a pointer in std::vector<Widget*> v;
    > ??
    >


    In your particular solution I don't see anything better - abstract base
    class allow you to access specialized types through unified interface
    (declared in base class). I strongly recommend you to read GoF's
    explanation of Composite, then may be you will get the idea behind it
    more clear.

    Note: you can use "smart pointers" instead of raw pointers. Then memory
    management is much simplier. Here they are:
    http://www.boost.org/libs/smart_ptr/smart_ptr.htm

    Cheers
    --
    Mateusz Åoskot
    http://mateusz.loskot.net
    =?UTF-8?B?TWF0ZXVzeiDFgW9za290?=, Dec 27, 2005
    #4
  5. Manuel

    Manuel Guest

    Mateusz Åoskot wrote:

    > You're welcome!

    [...]
    > Look at older thread titled "STL map and memory management (clear() )",
    > about Dec, 15.

    [...]
    >I strongly recommend you to read GoF's
    > explanation of Composite, then may be you will get the idea behind it
    > more clear.
    >
    > Note: you can use "smart pointers" instead of raw pointers. Then memory
    > management is much simplier. Here they are:
    > http://www.boost.org/libs/smart_ptr/smart_ptr.htm



    THANKS!!
    I'll study your suggestions and trying some experiments before boring
    you with other newbie questions.:)

    Best Regards,

    Manuel
    Manuel, Dec 27, 2005
    #5
  6. Manuel

    BobR Guest

    Manuel wrote in message <43b091a3$0$1078$>...
    >>Mateusz Åoskot wrote:


    >Some questions:
    >
    >1) What happen if I use v.clear() instead DeleteObject() ??


    You would leave objects in memory with no way to delete them. If you want to
    use 'v.clear()', you can do it this way:

    int main(){
    std::vector<Widget*> v;
    Button Btn;
    v.push_back( &Btn );
    Label Lbl;
    v.push_back( &Lbl );
    // .... etc. ....
    std::vector<Widget*>::const_iterator it;
    for (it = v.begin(); it != v.end(); ++it){
    (*it)->draw();
    }

    v.clear() // vector will empty at this point.
    Label Lbl2;
    v.push_back( &Lbl2 );
    v.clear() // vector will empty at this point.
    return 0;
    } // Btn, Lbl, etc. destructors will be invoked at this point.

    Also read what Mateusz suggested.

    Bruce Eckel, in his book 'Thinking in C++' vol 2, has another way to handle
    the pattern (which he calls the 'Command pattern' ). You can download a legal
    copy of the book here: http://www.bruceeckel.com/ [ref: //:
    C10:CommandPattern.cpp ]

    --
    Bob R
    POVrookie
    BobR, Dec 27, 2005
    #6
  7. Manuel

    Manuel Guest

    BobR wrote:

    > Bruce Eckel, in his book 'Thinking in C++' vol 2, has another way to handle
    > the pattern (which he calls the 'Command pattern' ). You can download a legal
    > copy of the book here: http://www.bruceeckel.com/ [ref: //:
    > C10:CommandPattern.cpp ]



    Thanks...a lot of things to read :)


    > POVrookie


    Curiosity: Are you POVray user?

    Ciao,

    Manuel
    Manuel, Dec 27, 2005
    #7
  8. Manuel

    Axter Guest

    Re: Problem: how to use one std container to store multiple types?

    Manuel wrote:
    > Mateusz Loskot wrote:
    >
    > > You're welcome!

    > [...]
    > > Look at older thread titled "STL map and memory management (clear() )",
    > > about Dec, 15.

    > [...]
    > >I strongly recommend you to read GoF's
    > > explanation of Composite, then may be you will get the idea behind it
    > > more clear.
    > >
    > > Note: you can use "smart pointers" instead of raw pointers. Then memory
    > > management is much simplier. Here they are:
    > > http://www.boost.org/libs/smart_ptr/smart_ptr.htm

    >
    >
    > THANKS!!
    > I'll study your suggestions and trying some experiments before boring
    > you with other newbie questions.:)


    I recommend you use a deep copy (clone) smart pointer, instead.
    http://code.axter.com/copy_ptr.h
    or a COW smart pointer
    http://code.axter.com/cow_ptr.h

    Both the copy_ptr and the cow_ptr can perform a deep copy when needed.
    Example usage:
    vector<copy_ptr<foo> > vFoo;
    vFoo.push_back(new FooDerived);

    You can use the boost::shared_ptr only if you're sure sharing the
    pointee will not negatively effect your code. In most cases, this is
    rarely what you want in a container.

    Boost also has a set of pointer containers, but the interface is poor,
    and does not match the STL containers very well.
    I recommend using copy_ptr or cow_pt instead.

    For more information, read the following related Article:
    http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c10407/

    In the above article it uses a clone_ptr smart pointer, but the
    copy_ptr is a more optimize and complete version of the clone_ptr class.
    Axter, Dec 27, 2005
    #8
  9. Manuel

    BobR Guest

    Manuel wrote in message <43b1097b$0$1071$>...
    >BobR wrote:
    >> Bruce Eckel, in his book 'Thinking in C++' vol 2, has another way to

    handle
    >> the pattern (which he calls the 'Command pattern' ). You can download a

    legal
    >> copy of the book here: http://www.bruceeckel.com/ [ref: //:
    >> C10:CommandPattern.cpp ]

    >
    >Thanks...a lot of things to read :)
    >
    >
    >> POVrookie

    >
    >Curiosity: Are you POVray user?


    Yes. I am still a POVray rookie because I have been spending more time
    learning C++.
    One of my current projects is for POVray (though it also outputs OGL, RAW,
    RAD, OBJ, TIN, RIB). It makes a 3D Rock (as triangle mesh) and
    displays/animates in a OpenGL window in wxWidgets.

    Some people think that POV is Point-Of-View, and I just answer them with a
    smiley-face :-}

    --
    Bob R
    POVrookie
    --
    POVray: http://www.povray.org/
    BobR, Dec 27, 2005
    #9
  10. Manuel

    Manuel Guest

    Can you explain me (please be patient) because this structure

    struct DeleteObject
    {
    template <typename T>
    void operator()(const T* ptr) const
    {
    delete ptr;
    }
    };

    instead a function like

    DeleteObject( *ptr){ delete ptr; };

    ??

    Thanks,

    Manuel
    Manuel, Dec 28, 2005
    #10
  11. Manuel

    Guest

    Re: Problem: how to use one std container to store multiple types?

    Manuel wrote:
    > Can you explain me (please be patient) because this structure
    >
    > struct DeleteObject
    > {
    > template <typename T>
    > void operator()(const T* ptr) const
    > {
    > delete ptr;
    > }
    > };
    >
    > instead a function like
    >
    > DeleteObject( *ptr){ delete ptr; };


    Easy: the function misses a type. Either you fix the type for
    'ptr', or you make a function template DeleteObject<T>.

    Now, if you want to delete every pointer in the range
    container.begin(), container.end(), you need to pass the
    correct deleter. The struct can delete all pointer types,
    but the function (or an instantiation of the function template)
    can delete only one type (in the functio template case,
    the type it's instantiated with).

    In practice, this means the function form needs a <TYPE>
    argument. This may be a complicated expression which
    must match exactly the type already known to the compiler.
    That's just a game of 'guess what I'm thinking', not useful.

    HTH,
    Michiel Salters
    , Dec 28, 2005
    #11
  12. Manuel wrote:
    > Can you explain me (please be patient) because this structure
    >
    > struct DeleteObject
    > {
    > template <typename T>
    > void operator()(const T* ptr) const
    > {
    > delete ptr;
    > }
    > };
    >


    This is a Function Object (Functor) - read about in a book or Web.
    Second, it also is a kind of "universal" Functor which can be
    specialized to call delete on all types of pointers.

    > instead a function like
    >
    > DeleteObject( *ptr){ delete ptr; };
    >


    This is a function (not complete definition, type is missing:
    void DeleteObject(MyClass *ptr){ delete ptr; };

    In the form above, it's (almost) like a specialized version of the
    Functor - operates on one and the only one type - specialized with MyClass.

    Template version is more flexible, you can use it for every*** type of
    pointers in your container - one Functor for all (almost***).


    *** - as you can see how delete operator is used then you see that it
    can not be used to delete arrays (delete[] pointer_to_array).

    Cheers
    --
    Mateusz Åoskot
    http://mateusz.loskot.net
    =?UTF-8?B?TWF0ZXVzeiDFgW9za290?=, Dec 28, 2005
    #12
  13. Manuel

    Manuel Guest

    Thanks!
    The functors are very advanced features, I must study a lot before fully
    understand them...
    However thanks to you now I've at least an idea about how the code work.
    Thanks,

    Manuel
    Manuel, Dec 28, 2005
    #13
  14. Manuel wrote:
    > Thanks!
    > The functors are very advanced features, I must study a lot before fully
    > understand them...


    Yup! They are worth to learn, definitely.
    Especially, when you use STL stuff like algorithms.

    > However thanks to you now I've at least an idea about how the code work.
    > Thanks,


    You're welcome.
    Cheers
    --
    Mateusz Åoskot
    http://mateusz.loskot.net
    =?UTF-8?B?TWF0ZXVzeiDFgW9za290?=, Dec 28, 2005
    #14
  15. Manuel

    Manuel Guest

    Ehm...I don't understad another error about this topic.
    Using the code below:

    ----------------------------
    #include <iostream>
    #include <vector>
    using namespace std;

    class mhwidget {
    public:
    virtual void draw() const = 0;
    };

    class square : public mhwidget {
    public:
    void draw(){cout << "square!";}
    };

    class MHwindow {
    vector<mhwidget*> widgetList;
    public:
    void addWidget(mhwidget* w) {cout << "added!";}
    };

    int main (){
    MHwindow MHgui;
    MHgui.addWidget(new square);
    return 0;
    }
    -----------------------------

    The compiler return this error:

    In function `int main()':
    cannot allocate an object of type `square'
    because the following virtual functions are abstract:
    virtual void mhwidget::draw() const


    Deleting "const" it work...I'm trying to find the reason of this on
    various books, but without success...

    Ciao!

    Manuel
    Manuel, Dec 28, 2005
    #15
  16. Manuel wrote:
    > Ehm...I don't understad another error about this topic.
    > Using the code below:
    >
    > ----------------------------
    > #include <iostream>
    > #include <vector>
    > using namespace std;
    >
    > class mhwidget {
    > public:
    > virtual void draw() const = 0;
    > };
    >


    OK, but most likely you need to add public virtual destructor:
    virtual ~mhwidget();

    > class square : public mhwidget {
    > public:
    > void draw(){cout << "square!";}
    > };


    Here is the problem.
    Look, this declaration

    void draw() const;

    is not the same as thisone:
    void draw();

    The former declares const-member-function, the latter
    non-const-member-function.
    And this is what your compiler does not like, the confusion :)
    So, you need to define it this way (as a const function):
    void draw() const {cout << "square!";}

    Cheers
    --
    Mateusz Åoskot
    http://mateusz.loskot.net
    =?UTF-8?B?TWF0ZXVzeiDFgW9za290?=, Dec 28, 2005
    #16
  17. Manuel

    Manuel Guest

    Mateusz Åoskot wrote:

    > The former declares const-member-function, the latter
    > non-const-member-function.
    > And this is what your compiler does not like, the confusion :)
    > So, you need to define it this way (as a const function):
    > void draw() const {cout << "square!";}



    Ouch!
    I'm stupid!
    Sorry for !!another!! silly question!
    Manuel, Dec 28, 2005
    #17
  18. Manuel wrote:
    > Mateusz Åoskot wrote:
    >
    >> The former declares const-member-function, the latter
    >> non-const-member-function.
    >> And this is what your compiler does not like, the confusion :)
    >> So, you need to define it this way (as a const function):
    >> void draw() const {cout << "square!";}

    >
    >
    >
    > Ouch!
    > I'm stupid!
    > Sorry for !!another!! silly question!


    I'd not say that. Just try to understand what compiler is saying and
    what his message means.
    Cheers
    --
    Mateusz Åoskot
    http://mateusz.loskot.net
    =?UTF-8?B?TWF0ZXVzeiDFgW9za290?=, Dec 28, 2005
    #18
  19. Manuel

    Manuel Guest

    Mateusz Åoskot wrote:

    > struct DeleteObject
    > {
    > template <typename T>
    > void operator()(const T* ptr) const
    > {
    > delete ptr;
    > }
    > };



    I've not fully understand functors yet.
    I'm trying to write a generic container class: can I declare the
    structure above into the class, and use it writing

    std::for_each(v.begin(), v.end(), container::DeleteObject());

    If yes...it work with any vector, even from differents classes?

    Thanks,

    Manuel
    Manuel, Dec 29, 2005
    #19
  20. Manuel

    Manuel Guest

    Manuel wrote:

    > I'm trying to write a generic container class: can I declare the
    > structure above into the class, and use it writing


    This is my experiments:

    ----------------------------------------------
    #include <windows.h>
    #include "mhcontainer.h"


    #include <iostream>//For debug

    struct DeleteObject
    {
    template <typename T>
    void operator()(const T* ptr) const
    {
    delete ptr;
    }
    };

    //Put widget into container
    void MHcontainer::addWidget(mhwidget* w) { widgetList.push_back(w); }

    //Draw all widgets
    void MHcontainer::drawAll()
    {
    std::cout << "size: " << widgetList.size();
    std::vector<mhwidget*>::iterator it = widgetList.begin();
    while(it != widgetList.end())
    {
    std::cout << "widget!";
    (*it++)->draw();
    }
    }

    //Delete all widgets
    void MHcontainer::deleteAll()
    {
    std::for_each(widgetList.begin(), widgetList.end(), DeleteObject());
    }
    ---------------------------------------


    where mhcontainer.h is:


    ---------------------------------------
    #include "mhwidget.h"
    #ifndef MHCONTAINER_H
    #define MHCONTAINER_H

    class mhcontainer
    {
    private:
    bool active = 1;
    std::vector<mhwidget*> widgetList;

    public:
    void setOn(){active = 1;}
    void setOff(){active = 0;}
    void addWidget(mhwidget*);
    void drawAll();
    void deleteAll();
    ~mhcontainer(){};
    };

    #endif //MHWIDGET_H

    ---------------------------------------

    Maybe OK?
    Manuel, Dec 29, 2005
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Vivi Orunitia
    Replies:
    11
    Views:
    4,473
    Martijn Lievaart
    Feb 4, 2004
  2. Steven T. Hatton
    Replies:
    4
    Views:
    3,896
    Rob Williscroft
    Dec 5, 2004
  3. Replies:
    4
    Views:
    798
    Daniel T.
    Feb 16, 2006
  4. Bart Simpson
    Replies:
    6
    Views:
    384
    Diego Martins
    Mar 13, 2006
  5. Simon
    Replies:
    2
    Views:
    489
    Simon
    May 31, 2009
Loading...

Share This Page