C++/C++

Advertisement


Question
How can we create runtime object in c++?

Explaination:
we just have the class name and from that i want to create an object of that class and use this object over and over in a program.

Answer
I am going to assume you mean that at runtime there is some data external to the code in which you have values that are the names of classes. Otherwise in the code you would just have used the name to create an instance of the class - either on the stack as a local automatic variable, in the static data area as local or global statics or dynamically when needed at runtime using new.

Well it can be done but all such schemes in C++ have to come down to creating an object dynamically and this means executing a line of the form:

       pObject = new ObjectType;

Of course you could use a new form that required arguments:

       pObject = new ObjectType( arg1, arg2 );

Once you have created such an object it exists until deleted or the program terminates – assuming the operating system cleans up un-allocated resources when a program (or rather its process) terminates.

But then in any generalised solution you have the problem of how many arguments and what are their types. So many solutions to this problem construct objects using their default constructor and leave further initialisation to other member functions - often called Create or similar - and this two-part setup is sometimes called two stage construction.

One powerful variation is to construct the objects using the default constructor then call a creation function passing it a std::istream reference so that it can initialise itself further directly from a stream - probably a file stream and the same stream used to obtain the object type name used to determine which object type to construct.

So, how do we map the object type name string to the relevant new statement? Well there are many ways to do this but one of the most obvious is to define a base (or root) object type that all such runtime creatable object types derive from - it could also define a standard interface in support of this role - such as a (pure) virtual Create member function taking a std::istream reference for example:

       class BaseRuntime
       {
       public:
         ~BaseRuntime () {}
         virtual void Create( std::istream & in ) = 0;
       };


Next, we need to route to the new statement using the class name. The most obvious solution is a function that takes the class name and returns a pointer to the base runtime type:

       BaseRuntime * NewObject( std::string const * className )
       {
         if ( className == "class1" )
         {
         return new class1;
         }
         else if ( className == "class2" )
         {
         return new class2;
         }
         else if ( className == "class3" )
         {
         return new class3;
         }
         // ...

         return NULL // unknown class name
       }


However this can become slow if many types are to be created from their string name – particularly for the names at the end of the if..else if.. sequence.

An alternative approach is for each type we wish to create dynamically we define a factory function or function object or a class having a known creation function. In the simplest scheme once again we define a base factory type which defines a (pure) virtual function that each sub-class implements. This function returns a pointer to the base or root runtime object type:

       class BaseFactory
       {
         ~BaseFactory() {}
         virtual BaseRuntime * MakeObject() = 0;
       };

Now each class for runtime creatable objects derives from BaseRuntime and provides a factory class derived from BaseFactory:

       class ARuntimeCreatableType : public BaseRuntime
       {
       // ...
       };

       class ARuntimeCreatableTypeFactory : public BaseFactory
       {
         virtual BaseRuntime * MakeObject();
       };

And the factory class implements MakeObject which just returns the result of new for the type it creates:

       BaseRuntime * ARuntimeCreatableTypeFactory::MakeObject()
       {
         return new ARuntimeCreatableType;
       }

Although each runtime creatable type is in itself specific, the code to create such a type is formulaic - so lends itself to being automated to some degree by creating a factory class template:

       template <class TRuntineType>
       class TRuntimeTypefactory : public BaseFactory
       {
         virtual BaseRuntime * MakeObject();
       };

       template <class TRuntineType>
       BaseRuntime * TRuntimeTypefactory::MakeObject()
       {
         return new TRuntineType;
       }

Now all we would need to do to create a factory is instantiate the relevant specialisation of TRuntimeTypefactory. For example we could define an explicit specialisation like so:

       template class TRuntimeTypefactory<ARuntimeCreatableType>;

Now we have the parts - but we still need to associate an id with a factory instance and a factory instance of the correct type. In your case the id is a name string.

The obvious candidate for this job is a map or dictionary of some sort. Luckily for us standard C++ now provides a map class template. So, what types do we need for this map? Well obviously we need an id type - a string for the class name in this case, and we need a factory object to create the object. However each factory is a distinct type, so we cannot just plonk in a factory instance - we need to store the same type and that needs to be polymorphic - and this means using a pointer or reference to the BaseFactory, and for our needs a pointer is the thing to use so we need a map keyed on strings and returning a pointer to a BaseFactory instance on which we can call MakeObject. So let us define a type alias - i.e. a typedef - for such a map:

       typedef std::map<std::string, BaseFactory*>  FactoryMapType;

Or, more generally:

       typedef std::map<TypeIdType, BaseFactory*>  FactoryMapType;

where TypeIdType might be a std::string, as in this case:

       typedef std::string  TypeIdType;

or maybe it is an unsigned integer of some sort:

       typedef unsigned long  TypeIdType;

or some other type such as a 128-bit UUID (universally unique identifier) (a.k.a GUID or globally unique identifier).

Now we have to consider how to create and cleanup the map and its contained keys and values. The std::string keys present no problems as they have value semantics and the map will destroy the instances it holds when it is destroyed and a std::string can be created from C/C++ string literal easily enough. The BaseFactory pointers are another matter. First we need to create the specific factory types and place a base pointer to them into the map as the values. Second, when the map is destroyed only the pointers are deleted (a no-op) not what they point to - so we have to explicitly delete the factory instances. This calls for another class to register each factory with the name of the class, and to clean it all up when destroyed. In fact such a registry can also encapsulate the tedious detail of looking up a factory by name in the map and creating a new object, here is an example similar to one I am using at the moment:

       class FactoryRegister
       {
       public:
         virtual         ~FactoryRegister();

         BaseRuntime *   NewObject( TypeIdType typeId );

         void          RegisterFactory( TypeIdType typeId
         , FactoryBase * factory
         );

       private:
         typedef   std::map<TypeIdType, FactoryBase*>   
         FactoryMapType;

         FactoryMapType   Factories_;
       };

The destructor is virtual because in the context I am using it the registrars are also other types so these types use FactoryRegister as a mix-in base class. You will notice that the names are those in this example – I had to change only a few of them as some were in fact the same! I use a TypeIdType as I showed previously – which in my case is an unsigned long but in your case would be a std::string or other string type compatible with use in a standard C++ associative container.

The destructor is used to iterate through the map deleting each factory before the map is destroyed:

       FactoryRegister::~FactoryRegister()
       {
         std::for_each( Factories_.begin()
         , Factories_.end()
         , DeleteFactory
         );
       }

DeleteFactory just deletes each factory:

       void DeleteFactory(std::pair<ConstTypeIdType, FactoryBase*> & item)
       {
         delete item.second;
       }

The rather complex type of the argument is due to the fact that std::map types store values that are a pair consisting of the key and the value of a map item. std::pair is a C++ library utility class template.

To add a class name and factory to a register you call the RegisterFactory member function which just inserts the class name key and the factory pointer into the map:

       void FactoryRegister::RegisterFactory
       ( TypeIdType typeId
       , FactoryBase * factory
       )
       {
         Factories_.insert( std::make_pair(typeId, factory) );
       }

To do this you have to create a new factory of the correct type and pass the pointer to FactoryRegister::RegisterFactory – a tedious task which can be made easier with a bit more templating:

       template <class T>
       class RegisterFactory
       {
       public:
         RegisterFactory( FactoryRegister & factoryRegister
         , TypeIdType typeId
         )
         {
         FactoryBase * pFactory = new TRuntimeTypefactory <T>;
         factoryRegister.RegisterFactory( typeId, pFactory );
         }
       };

This class template has only a constructor which takes a reference to the register to which the factory is to be added (my case requires more than one registry), and the id (class name) the factory is associated with. This constructor creates a new factory for the specified runtime type T – the single template parameter. It then passes this together with the id (class name) value to the passed in registry's RegisterFactory member function. You can then register a class for runtime creation like so:

RegisterFactory<ARuntimeCreatableType>(theRegistry, "ARuntimeCreatableType" );

This was adapted from some real current code of mine.

Of course if you do not need more than one registry then you could use the one and only registry directly in the RegisterFactory::RegisterFactory constructor rather than passing it in as a parameter. Then the registration would look like:

RegisterFactory<ARuntimeCreatableType>( "ARuntimeCreatableType" );

You would add such registration ‘calls' to some suitable early call point in your application – in my case the application is a MS MFC application so these calls were performed during the MFC application object's InitInstance call.

Finally, to create a new object you call the FactoryRegister::NewObject member function which returns a pointer to a new BaseRuntime object – the specific type being dependent upon the id value – i.e. class name value - passed to NewObject. NewObject uses the id to look up the factory used to create an object and calls its MakeObject member function thus:

       BaseRuntime * FactoryRegister::NewObject( TypeIdType typeId )
       {
         BaseRuntime * pObject = NULL;

         FactoryMapType::iterator pos = Factories_.find( typeId );

         if ( pos != Factories_.end() && pos->second != NULL )
         {
         pObject = pos->second->MakeObject();
         }

         return pObject;
       }

Now your head is probably reeling at the moment at all this code just to create an object of the correct type as indicate by a string read or obtained somehow at runtime. Some of the code – such as RegisterFactory and FactoryRegister  are just helpers – they do make using the factory map much easier but are not really necessary.

Note that all the above code has not been compiled so may contain typos and inconsistencies – my apologies in advance for any such errors.

Finally I have only shown a couple of ways to approach this problem – there are many others and which one is good for you depends on the particular circumstances.

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.