Thread: Declaring instances of different implementations

  1. #1
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057

    Declaring instances of different implementations

    This is my first foray into C++ in a long time, so please go easy on me.

    I'm trying to create several abstract base classes, which I will then extend and implement in several different ways. Most of the time only one of these implementations will be used -- I'm mostly writing wrappers for other libraries, and the libraries are direct replacements for each other. So I might have an XML reader abstract class, for example, and two implementations of this class, for libexpat and libmxml; but, of course, only one class needs to be compiled with the program.

    So, my problem. I want other parts of the program to be able to declare instances of the abstract class. Of course this can't be done, but . . . well . . . here's what I mean.

    Base class:
    Code:
    class graphics {
    protected:
        bool quit_requested;
        int framerate;
    public:
        static const int DEFAULT_FRAMERATE;
    public:
        graphics();
        virtual ~graphics() {}
        
        virtual void init() = 0;
        virtual void get_events() = 0;
        virtual void repaint() = 0;
        
        virtual void update(bool all = 0) = 0;
        virtual void clear_screen() = 0;
        virtual void delay() = 0;
        
        virtual void set_framerate(int rate = DEFAULT_FRAMERATE)
            { framerate = rate; }
        virtual int get_framerate() const { return framerate; }
        
        virtual void set_quit_requested(bool quit = false)
            { quit_requested = quit; }
        virtual bool get_quit_requested() const { return quit_requested; }
    };
    Implementation class for the SDL (yes, I went overboard with namespaces):
    Code:
    class sdl : public mundus::graphics::graphics {
    private:
        SDL_Surface *screen;
        FPSmanager fpsm;
    public:
        sdl();
        virtual ~sdl();
        
        virtual void init();
        virtual void get_events();
        virtual void repaint();
        
        virtual void update(bool all = 0);
        virtual void clear_screen();
        virtual void delay();
        
        virtual void set_framerate(int rate = DEFAULT_FRAMERATE);
        
        static SDL_Rect build_sdl_rect(rect &rect);
    private:
        void handle_event(SDL_Event &event);
    };
    Code that uses a graphics class:
    Code:
    class mundus {
    protected:
        graphics::sdl::sdl graphics;
        object objects;
    public:
        void run();
        
        object &get_objects() { return objects; }
    };
    
    // ...
    
    void mundus::run() {
        graphics.init();
        
        for(;;) {
            graphics.get_events();
            if(graphics.get_quit_requested()) break;
            
            graphics.clear_screen();
            
            objects.repaint();
            
            graphics.repaint();
            graphics.delay();
        }
    }
    The trouble, of course, is that I don't want to declare an instance of sdl in the mundus class like this:
    Code:
    graphics::sdl::sdl graphics;
    Or even like this:
    Code:
    graphics::graphics *graphics = new graphics::sdl::sdl();
    I want to declare an instance of the graphics class, using whatever implementation is available. Of course, another implementation would likely have a different name, and so the declaration I'm using at the moment would be invalid.

    Any thoughts on how to do this?

    My thoughts have ranged from creating a base "creation" class that would know how to construct every class type (difficult and error-prone), to using macros (ugh), to implementing the base class directly instead of extending it (what if I want to add other members to the class?), to not bothering with a base class at all, but just having different implementations compatible (too ad-hoc). I guess overloading the new operator might work too -- but I think I'm looking for overly complicated solutions here.

    Any thoughts on my design? Inheritance might not be the best way to go, it's just what first came to mind. It makes sense, but it's making declaring objects difficult.

    End note: mundus is the name of the project. It means universe (amongst other things) in Latin.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  2. #2
    Registered User
    Join Date
    Apr 2008
    Posts
    890
    Create an Abstract Factory and have it decide what graphics library to use and create (probably based on user input?).

  3. #3
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You mean like this?
    Code:
    graphics *g;
    
    if(wants_sdl()) g = new sdl();
    else if(wants_ncurses()) g = new ncurses();
    else g = new text_based();
    The main problem with a creator class like this is that I wanted to be able to add implementations without touching the main code. Which, of course, is basically impossible unless I use a macro or something to modify the main code . . . .

    It might work, though.

    Another problem with this is that I have many classes that are being implemented in this way -- graphics, image, text, font, resource, etc. I don't fancy creating a separate class for each one just to create an instance of each class.

    I suppose having one monolithic creator class might work, though. I could even put it in a header file so that the class could be modified by the build system to choose the right implementations.

    I'll have to try that and see what happens.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  4. #4
    Registered User
    Join Date
    Apr 2008
    Posts
    890
    Google Abstract Factory, and Factory Method as well. Essentially, your client classes will use one of these to return a reference or smart pointer to a graphics api (abstract base class), perhaps based on a choice the user makes at runtime (the Irrlicht 3D demos do this), or a value in a config file, etc.

    Sorry I didn't give a more thoughtful answer...in a hurry to get out the door.

  5. #5
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    That looks like it might be useful. Thanks.

    Sorry I didn't think to Google "Abstract Factory". I suppose it was capitalized for a reason.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You will still have to compile the unused implementation in your project. Since it is possible that either of the two implementations of the interface could be used you must compile them both. However if you know for a fact that one certain impl will be used you can just remove the other impl from your project.

    But based on your statements you would need something like a switch statement that would decide which impl to instantiate and then return it. This is essentially the design pattern behind a factory but there are many ways to implement it. I prefer the switch statement since it is straightforward but there are other ways.

  7. #7
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You will still have to compile the unused implementation in your project. Since it is possible that either of the two implementations of the interface could be used you must compile them both. However if you know for a fact that one certain impl will be used you can just remove the other impl from your project.
    Yes, this is what I was originally thinking of doing, using the build system to compile in support for only one library -- but I actually rather like the idea of one executable being able to use different implementations, so I may use a switch or something.

    I've started implementing a factory of sorts. It's rather complicated because I have many different classes that need to have a certain implementation instantiated. It's working pretty well for now, but of course I only have one implementation for each class right now.

    We'll have to see how it holds up when I actually create another implementation.

    Thank you for your help -- it would have been adhocier without you. :P
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Yes, this is what I was originally thinking of doing, using the build system to compile in support for only one library -- but I actually rather like the idea of one executable being able to use different implementations, so I may use a switch or something.
    Yes for production code I opt for one exe being able to instantiate different implementations. Usually the variable that determines this is grabbed from a config file of sorts. Switch on this variable and you now have an exe that can do both of the impls.

    I also see value though in simply removing the other impl from the project. This may be preferable if you have clients that you will only be providing one impl to. After all they cannot gain functionality by altering precious config files if the source code for the impl isn't there.

    I guess it depends on the circumstances (and most notably the PM's and Lead(s) preferences) as to which approach I would take.
    Last edited by VirtualAce; 07-15-2008 at 09:12 PM.

  9. #9
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Quote Originally Posted by Bubba View Post
    Yes for production code I opt for one exe being able to instantiate different implementations. Usually the variable that determines this is grabbed from a config file of sorts. Switch on this variable and you now have an exe that can do both of the impls.
    It makes sense. The only trouble will be deciding which config-file-reading implementation to use. (I'm just kidding; those implementations should be identical, so I'd just pick the first one a shared libary is available for.)

    I also see value though in simply removing the other impl from the project. This may be preferable if you have clients that you will only be providing one impl to. After all they cannot gain functionality by altering precious config files if the source code for the impl isn't there.
    This would make sense if an implementation was especially large as well, I suppose.

    I guess it depends on the circumstances (and most notably the PM's and Lead(s) preferences) as to which approach I would take.
    Well, in this case, those people are me and me. It's just a personal project. But I know what you mean.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. "Selecting" Instances of Classes
    By SnS CEO in forum C++ Programming
    Replies: 12
    Last Post: 08-04-2005, 07:50 PM
  2. Classes and Destroyed Instances
    By FWGaming in forum C++ Programming
    Replies: 3
    Last Post: 07-12-2005, 10:02 PM
  3. Multiple instances of form resource
    By knutso in forum Windows Programming
    Replies: 4
    Last Post: 05-25-2004, 03:47 PM
  4. Declaring Pointers/Passing Data
    By Ryan_P in forum C++ Programming
    Replies: 10
    Last Post: 12-18-2002, 02:49 PM
  5. instances of a structure in a structure
    By wjday in forum C Programming
    Replies: 1
    Last Post: 10-10-2002, 10:12 AM