"new" type set at runtime?


DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 7 of 7

Thread: "new" type set at runtime?

  1. #1
    Join Date
    Jan 2009
    Posts
    9

    Question "new" type set at runtime?

    I have several classes, which we'll call classA, classB and classC to keep things simple, all of which inherit from a single class, which we'll call baseClass. In my code I want to instantiate an object of one of the child types, based on what the user clicks on. So if they click "classA" I want to instantiate an object of type classA, etc. The way I have it working at the moment is that I have a pointer of type baseClass, and the user selection is stored in a string. The string is then run through an if statement which selects the appropriate new statement, something like the sudo-code below:

    Code:
    baseClass * newClass;
    char selection[]=<user_selection>;
    if (selection=="classA")
        newClass=new classA;
    else if(selection=="classB")
        newClass=new classB;
    else if(selection=="classC")
        newClass=new classC;
    //...etc...
    else
       //throw some sort of error here
    While this works, it is obviously ugly and not very scaleable. Is there some way I can just make some sort of data structure and just do something like newClass=new classes[selection];? Thanks.

  2. #2
    Join Date
    Jan 2005
    Location
    UK
    Posts
    604
    look up "factory pattern" on google.
    Some way down the line you still have to do a if/case - switch, but you can do it in a standard way...
    DKyb
    -------------------------------
    Life is a short warm moment -
    Death is the long cold rest.
    Pink Floyd
    -------------------------------

  3. #3
    Join Date
    Dec 2003
    Posts
    3,366
    Clearly, you have an event driven system where the user clicks and that click is handled by handler-code? If so, why not add the item then and there, instead of creating a string, passing it back out, and handling the action associated with the click in some other odd location? If you have a good reason to not do this, thats ok, but if you can, do the work in the handler without the extra code (there is no reason for the string & string parse, its just extra code to maintain and debug unless there is a very good reason to have this stuff). This bypasses the need for checks too --- if the user clicked addClassA button, and you are in the handler for that button, then you add class A, no way that could be invalid (as your string must check for bad values), so you eliminate that mess too.

    We really need to understand your needs better, but you can also make a container for the 3 (or N) or whatever classes too, and then use some sort of storage for that. Or you can have a container of each type seperate, if that fits your design better. If you set the classes up properly, you can store them in vector, list, etc. if that fits your needs, just be sure the vector functions that you need are enabled by your class (for example, if you need to use std::sort, you need to create comparison operators and more).

  4. #4
    Join Date
    Jan 2005
    Location
    UK
    Posts
    604
    I have to disagree with jonnins' approach for one reason (there might be more or better ones):
    If you create your business objects in your GUI-handlers you cement an architecture that will be very difficult to break up later. It contravenes layering of your application with all the problems that come with it:
    - scalability impact
    - maintainance problems
    - code duplication
    - ...
    I would always, even for small projects, layer my application at least into GUI I/O and business objects, because from experience small (unimportant) projects have a tendency to grow in importance.
    By delegating the creation of your business objects to a different module you separate concerns which ensures that minimal code has to be touched if you need to amend/extend your application.
    DKyb
    -------------------------------
    Life is a short warm moment -
    Death is the long cold rest.
    Pink Floyd
    -------------------------------

  5. #5
    Join Date
    Dec 2003
    Posts
    3,366
    Drybelk is 100% correct, but I still say that you can avoid a string, and possibly the switch statement, as you see fit.

    The handler for the gui object has to do *something* such that the program can process the event. So what will it do? Create a string and parse it (current approach)? Call a function that is portable (my preferred approach to GUI code)? Something else? If you go with just having it call another function, what does *that* function do? This is where I say you could just create the object, instead of the a string, to remove both the string and the case statements. If you want the code all in one place, or to layer it up in something like the current design, the function you call can take a parameter(enum, please!) and then you can drop the switch statement around that, and still avoid the string. I argue that the work done to change the code later would be no more than using other methods --- add a new enum value, add that to your case list, add a new handler, call function with new enum parameter, and its the same as using a string to convey the data but more efficient and less confusing to read. Perhaps that is all I am really saying, in the end, is replace your string with an enum, but if you wanted to put each object creation in a seperate function and call that from the handlers without the parameter, that is also reasonable (its the exact same code, you replace the enum creation & case filler statements with function headers to make several tiny functions --- the volume of code is pretty much the same and the maintence is very similar (add a function, add a case statement, its about the same work).

  6. #6
    Join Date
    Jan 2009
    Posts
    9
    Thanks for the responses. I hadn't thought of the enum approach, but I definitely like it as it will (I think, at least) allow me to use a switch statement rather than a bunch of ifs, and make things cleaner. As far as even handlers go, there is only one that I am working with here, so breaking things out into separate event handlers each of which creates an object of the proper type wouldn't be an option. The way things are set up, the user selects the value from a pull-down list rather than clicking individual buttons, so the only event is the list selection, and it is the same event no matter what the user selects- thus the need to look at a string (or something) to determine what was just selected. Using an enum should definitely make things nicer though. I'll have to look up that 'factory pattern' thing as well - that's not something I am familiar with :) Thanks again!

  7. #7
    Join Date
    Dec 2007
    Posts
    401
    one of the cononical ways to address this problem is to use the exemplar (aka protype) pattern.

    a good discussion on implementing exemplars in C++ is available in:
    'Advanced C++ Programming Styles and Idioms' by James O. Coplien
    http://www.amazon.com/Advanced-C-Pro.../dp/0201548550
    see chapter 8: Programming with Exemplars in C++ pages 279-306

    a brief introduction: http://allpatterns.blogspot.com/2006/12/prototype.html

    the skeleton would look like this:
    base_class.h
    Code:
    #ifndef BASE_H_INCLUDED
    #define BASE_H_INCLUDED
    #include <string>
    
    struct base_class
    {
      virtual ~base_class() ;
      base_class() ;  // normal constructor
    
      // create a new object of the same type if class_name matches
      virtual base_class* create_another( const std::string& class_name ) = 0 ; 
      // ...
    
      // create a new object of the right derived class
      static base_class* create( const std::string& class_name )  ;
    
      protected :
        base_class(int) ; // constructor to create exemplars
                          // the int argument is used only to colour the constructor
    };
    
    #endif // BASE_H_INCLUDED

    base_class.cc
    Code:
    #include "base_class.h"
    
    namespace
    {
      int exemplar_count = 0 ;
      enum { MAX_DERIVED_CLASSES = 1024 } ;
      base_class* exemplars[  MAX_DERIVED_CLASSES ] = { 0 } ;
    }
    
    base_class::~base_class() {}
      
    base_class::base_class() {}  // normal constructor
    
    base_class::base_class(int) // constructor to create exemplars
    {
      exemplars[  exemplar_count ] = this ;
      ++exemplar_count ;
    }
    
    // create a new object of the right derived class
    base_class* base_class::create( const std::string& class_name )  
    {
      base_class* object = 0 ;
    
      // iterate through the exemplars to see if the class matches one of them
      for( int i=0 ; i<exemplar_count ; ++i )
        if( ( object =  exemplars[i]->create_another( class_name ) ) != 0 ) break ;
    
      return object ;
    }
    class_A.cc ( there is no class_A.h! )
    Code:
    #include "base_class.h"
    #include <iostream>
    
    namespace
    {
      struct class_A : base_class
      {       
        class_A() { std::cout << "object of type class_A created\n" ; }
        class_A( int )  : base_class(1) {} // constructor to create exemplars 
    
        // create a new objectof the same type if class_name matches
        virtual base_class* create_another( const std::string& class_name )
        {
          if( class_name == "class_A" ) return new class_A() ;
          else return 0 ; 
        }
      };
    
    }
    
    class_A exemplar_object_for_class_A(1) ; // use the coloured constructor
    class_B.cc ( there is no class_B.h! )
    Code:
    #include "base_class.h"
    #include <iostream>
    
    namespace
    {
      struct class_B : base_class
      {       
        class_B() { std::cout << "object of type class_B created\n" ; }
        class_B( int )  : base_class(1) {} // constructor to create exemplars 
    
        // create a new objectof the same type if class_name matches
        virtual base_class* create_another( const std::string& class_name )
        {
          if( class_name == "class_B" ) return new class_B() ;
          else return 0 ; 
        }
      };
    
    }
    
    class_B exemplar_object_for_class_B(1) ; // use the coloured constructor

    main.cc to test it
    Code:
    #include "base_class.h"
    #include <iostream>
    
    int main()
    {
      base_class* one = base_class::create( "class_A" ) ;
      base_class* two = base_class::create( "class_B" ) ;
      base_class* three = base_class::create( "there_is_no_such_class" ) ;
    
      std::cout << one << ' ' << two << ' ' << three << '\n' ;
    }
    to identify the class, you could use an enum instead of a string (trade loose coupling for efficiency).

Similar Threads

  1. AccessForm Type Mismatch
    By TedW in forum VB Classic
    Replies: 4
    Last Post: 10-27-2006, 11:21 AM
  2. Access
    By Spumbu1977 in forum VB Classic
    Replies: 2
    Last Post: 09-07-2006, 09:56 PM
  3. RUNTIME ERROR saving a recordset
    By Des in forum VB Classic
    Replies: 5
    Last Post: 05-15-2002, 04:16 PM
  4. Dynamic Query in SQL7 called from VB6/ADO
    By Paul Moss in forum VB Classic
    Replies: 2
    Last Post: 03-28-2002, 06:46 PM
  5. No Backwards Fetching
    By Brian in forum VB Classic
    Replies: 3
    Last Post: 01-07-2001, 06:14 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center