Problem implementing an object factory

S

Stephen Torri

Here is my attempt at implementing a object factory. The purpose of this
is to replace a large switch statement in a factory class with the
functors. I get an error at line 88, marked, "expected primary-expression
before ')' token". I am using Modern C++ Design chapter 8 as a guide.

Stephen

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

#include <map>
#include <iostream>

namespace tool
{
namespace component
{
class Component;
}

namespace infrastructure
{

class Component_Factory
{
public:
typedef component::Component* (*CreateComponentCallback)();

bool RegisterComponent ( int component_id,
CreateComponentCallback call_func )
{
return m_callbacks.insert ( CallbackMap::value_type ( component_id,
call_func ) ).second;
}

bool UnregisterComponent ( int component_id ) {
return m_callbacks.erase ( component_id ) == 1;
}

component::Component* CreateComponent ( int component_id ) {
CallbackMap::const_iterator pos = m_callbacks.find ( component_id );

if ( pos == m_callbacks.end() )
{
std::cerr << "Unknown Component ID" << std::endl; abort();
}

return (pos->second)();
}

private:
typedef std::map<int, CreateComponentCallback> CallbackMap;

CallbackMap m_callbacks;
};

}

namespace component
{
class Component
{};


class Apple : public Component
{
public:

static const int ID = 1;

Component* operator()()
{
return new Apple;
}
};

class Blueberry : public Component
{
public:

static const int ID = 5;

Component* operator()()
{
return new Blueberry;
}
};
}
}


int main (int, char**)
{
tool::infrastructure::Component_Factory fact_ref;

/* LINE 88 */
fact_ref.RegisterComponent ( tool::component::Apple::ID,
tool::component::Apple );

fact_ref.UnregisterComponent ( tool::component::Apple::ID );

return 0;
}
 
B

Barry

Stephen said:
Here is my attempt at implementing a object factory. The purpose of this
is to replace a large switch statement in a factory class with the
functors. I get an error at line 88, marked, "expected primary-expression
before ')' token". I am using Modern C++ Design chapter 8 as a guide.

Stephen

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

#include <map>
#include <iostream>

namespace tool
{
namespace component
{
class Component;
}

namespace infrastructure
{

class Component_Factory
{
public:
typedef component::Component* (*CreateComponentCallback)();

bool RegisterComponent ( int component_id,
CreateComponentCallback call_func )
{
return m_callbacks.insert ( CallbackMap::value_type ( component_id,
call_func ) ).second;
}

bool UnregisterComponent ( int component_id ) {
return m_callbacks.erase ( component_id ) == 1;
}

component::Component* CreateComponent ( int component_id ) {
CallbackMap::const_iterator pos = m_callbacks.find ( component_id );

if ( pos == m_callbacks.end() )
{
std::cerr << "Unknown Component ID" << std::endl; abort();
}

return (pos->second)();
}

private:
typedef std::map<int, CreateComponentCallback> CallbackMap;

CallbackMap m_callbacks;
};

}

namespace component
{
class Component
{};


class Apple : public Component
{
public:

static const int ID = 1;

Component* operator()()
{
return new Apple;
}
};

class Blueberry : public Component
{
public:

static const int ID = 5;

Component* operator()()
{
return new Blueberry;
}
};
}
}


int main (int, char**)
{
tool::infrastructure::Component_Factory fact_ref;

/* LINE 88 */
fact_ref.RegisterComponent ( tool::component::Apple::ID,
tool::component::Apple );

the second param of Registercomponent is typeof
typedef component::Component* (*CreateComponentCallback)();
which is a free function.
now you trying to assign it with a class type, which is never valid
anywhere to use type as a function actual parameter.

so a quick and simple change is
> class Apple : public Component
> {
> public:
>
> static const int ID = 1;
>
> Component* operator()()
static Component* Create() // factory method
> {
> return new Apple;
> }
> };
> fact_ref.RegisterComponent ( tool::component::Apple::ID,
> tool::component::Apple );

fact_ref.RegisterComponent ( tool::component::Apple::ID,
&tool::component::Apple::Create );
 
S

Stephen Torri

the second param of Registercomponent is typeof typedef
component::Component* (*CreateComponentCallback)(); which is a free
function.
now you trying to assign it with a class type, which is never valid
anywhere to use type as a function actual parameter.

Thanks.

Is there a way to have each class that will be a part of a object factory automatically register itself?

Stephen
 
B

Barry

Stephen said:
Thanks.

Is there a way to have each class that will be a part of a object factory automatically register itself?

it depends on how you define "automatically",
if you meant register as you write the class, I think there's not.
if you meant easier registering, maybe you can apply macro, then you can
write less code, but since your code is not so complicated and the
reg/unreg is clean and easy to use, moreover, the macro is so notorious,
so don't fix it if it ain't broken.
 
J

James Kanze

[...]
it depends on how you define "automatically",
if you meant register as you write the class, I think there's not.
if you meant easier registering, maybe you can apply macro, then you can
write less code, but since your code is not so complicated and the
reg/unreg is clean and easy to use, moreover, the macro is so notorious,
so don't fix it if it ain't broken.

If he means that the user of the factory doesn't have to do
anything, the usual solution is to use the singleton idiom for
the factory (to avoid problems in order of initialization), and
then use static objects to register the the factories with the
map; if the map maps to functional objects, rather than to
pointers to functions, you need a static object for each type
anyway, so you might as well put the registration code in the
constructor.

An alternative solution would be to simply use a staticly
initialized table of pointers, using std::find_if to find the
factory. This is particularly good if the factories are
functions, and not functional objects, since it means that the
dynamic factory is entirely staticly initialized, and can be
used from constructors of other static objects without order of
initialization worries. If you do this, I would suggest using
some sort of automatic code generation for the table, so that
you don't have to re-edit it (with the risk of forgetting to do
so) each time you add a function. (And of course, it means that
the user of the dynamic factory can't add additional functions
of his own. Depending on what you're using the dynamic factory
for, this can be an advantage or a disadvantage.)
 
S

Stephen Torri

If he means that the user of the factory doesn't have to do
anything, the usual solution is to use the singleton idiom for
the factory (to avoid problems in order of initialization), and
then use static objects to register the the factories with the
map; if the map maps to functional objects, rather than to
pointers to functions, you need a static object for each type
anyway, so you might as well put the registration code in the
constructor.

Was this what you had in mind? I tried to create a singleton using the example I found in Modern C++ Design.
I am not sure about the private copy constructor or assignment operator.

Stephen

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


#include <map>
#include <iostream>

namespace tool
{
namespace component
{
class Component
{};


class Apple : public Component
{
public:

static const int ID = 1;

static Component* Create()
{
return new Apple;
}

};

class Blueberry : public Component
{
public:

static const int ID = 5;

static Component* Create()
{
return new Blueberry;
}
};
}

namespace infrastructure
{

class Component_Factory
{
public:
typedef component::Component* (*CreateComponentCallback)();

static Component_Factory& Instance()
{
static Component_Factory obj;
return obj;
}

bool RegisterComponent ( int component_id,
CreateComponentCallback call_func )
{
return m_callbacks.insert ( CallbackMap::value_type ( component_id, call_func ) ).second;
}

bool UnregisterComponent ( int component_id )
{
return m_callbacks.erase ( component_id ) == 1;
}

component::Component* CreateComponent ( int component_id )
{
CallbackMap::const_iterator pos = m_callbacks.find ( component_id );

if ( pos == m_callbacks.end() )
{
std::cerr << "Unknown Component ID" << std::endl;
abort();
}

return (pos->second)();
}

private:
typedef std::map<int, CreateComponentCallback> CallbackMap;

CallbackMap m_callbacks;

Component_Factory()
{
this->RegisterComponent ( tool::component::Apple::ID, &tool::component::Apple::Create );
this->RegisterComponent ( tool::component::Blueberry::ID, &tool::component::Blueberry::Create );
}

Component_Factory ( Component_Factory const& rhs )
{
//m_callbacks ( rhs.m_callbacks.begin(), rhs.m_callbacks.end() );
}

Component_Factory& operator= ( Component_Factory const& rhs )
{
Component_Factory temp ( rhs );
swap ( temp );

return *this;
}

void swap ( Component_Factory& rhs )
{
std::swap ( m_callbacks, rhs.m_callbacks );
}
};

}

}


int main (int, char**)
{
tool::component::Component* apple_ptr = tool::infrastructure::Component_Factory::Instance().CreateComponent ( tool::component::Apple::ID );

std::cout << "ID = " << static_cast<tool::component::Apple*>(apple_ptr)->ID << std::endl;

tool::component::Component* berry_ptr = tool::infrastructure::Component_Factory::Instance().CreateComponent ( tool::component::Blueberry::ID );

std::cout << "ID = " << static_cast<tool::component::Blueberry*>(berry_ptr)->ID << std::endl;

tool::infrastructure::Component_Factory::Instance().UnregisterComponent ( tool::component::Apple::ID );
tool::infrastructure::Component_Factory::Instance().UnregisterComponent ( tool::component::Blueberry::ID );

delete apple_ptr;
delete berry_ptr;

return 0;
}
 
J

James Kanze

Was this what you had in mind? I tried to create a singleton
using the example I found in Modern C++ Design.

Not really. You seem to have missed the reason why I suggested
a singleton.

In my own code, I use two different techniques for managing
dynamic factories, depending on the requirements. The most
frequent uses dynamic registration, and is something like:

class Object // but obviously, with a better, domain
{ // specific name...
// interface definition...
} ;

class AbstractFactory
{
public:
explicit AbstractFactory( std::string const&
name ) ;
virtual ~AbstractFactory() {}
Object* createObject() const = 0 ;
} ;

class DynamicFactory : private boost::uncopiable
{
public:
static DynamicFactory&
instance() ;
Object* create( std::string const& name ) const ;
void enrol( std::string const& name,
AbstractFactory const& factory ) ;

private:
DynamicFactory() ;
typedef std::map< std::string, AbstractFactory const* >
Map ;
Map myMap ;
} ;

AbstractFactory::AbstractFactory(
std::string const& name )
{
DynamicFactory::instance().enrol( name, this ) ;
}

DynamicFactory&
DynamicFactory::instance()
{
static DynamicFactory*
theOneAndOnly
= new DynamicFactory ;
return *theOneAndOnly ;
}

Object*
DynamicFactory::create(
std::string const& name ) const
{
Map::const_iterator entry = myMap.find( name ) ;
return entry == myMap.end()
? NULL
: entry->second->createObject() ;
}

void
DynamicFactory::enrol(
std::string const& name,
AbstractFactory const*
factory )
{
assert( myMap.find( name ) == myMap.end() ) ;
myMap.insert( Map::value_type( name, factory ) ) ;
}

DynamicFactory::DynamicFactory()
{
}

template< typename T >
class Factory : public AbstractFactory
{
public:
explicit Factory( std::string const& name )
: AbstractFactory( name )
{
}

Object* createObject() const
{
return new T ;
}
} ;

The implementation of each derived SomeObject then contains a
static variable:

namespace {

Factory< SomeObject >
f( "SomeObject" ) ;
}

Which objects the factory knows how to build is only determined
at link time. In fact, this can be made very dynamic, using
dynamic linking; just establish a naming convention between the
object and the dynamically linkable file which contains it, and
modify the create function so that it loads this file ("dlopen"
under Unix), then retries, if it doesn't find the entry in the
map. (I use this technique a lot for things like input
character translation: it allows me to add support for new
encodings long after the original program has been linked and
delivered.)

The disadvantage of this technique is that it constructs the
factory map dynamically, during initialization, so the factory
cannot be used in constructors of static objects. That's not
usually a problem, but if it is, some other solution must be
used. In such cases, I'll usually use functions, instead of
objects, for the individual factories, and a table with the
structure:

// Defined in DynamicFactory...
struct Map
{
char const* name ;
Object* (* create)() ;

bool operator==( Map const& other ) const
{
return name == other.name ;
}

bool operator!=( Map const& other ) const
{
return name != other.name ;
}
} ;
static Map myMap[] ;
static size_t myMapSize ;

// Automatically generated file:
#include "DynamicFactory.hh"
extern Object* createSomeObject() ;
extern Object* createAnotherObject() ;
// ...

DynamicFactory::Map DynamicFactory::myMap[] =
{
{ "SomeObject", &createSomeObject },
{ "AnotherObject", &createAnotherObject },
// ...
} ;
size_t DynamicFactory::myMapSize = N ;
// Note that the initializer N
// is generated automatically,
// by simply counting the
// entries as we generated
// them.

This file is easily generated in a makefile by means of a simple
AWK script, which is fed the list of object types (possibly in
the form of source file names---again, a naming convention is
necessary). The enrol function disappears, and the create
function becomes:

Object*
DynamicFactory::create(
std::string const& name ) const
{
Map const* entry
= std::find( myMap, myMap + myMapSize, name ) ;
return entry == myMap + myMapSize
? NULL
: (*entry->create)() ;
}

Obviously, this doesn't allow any form of dynamic linking,
although adding or removing an object type is a simple matter of
modifying the makefile (which must be done anyway), and doesn't
require any modification in existing code. And the table is
fully statically initialized, which means that it is guaranteed
to be initialized before any dynamic initialization
(non-trivial constructors of static objects) occur. Although
I've not actually needed this technique for a dynamic factory
yet, I've used it in a number of similar cases where I've needed
a mapping which was available during the initialization of
static objects (mapping enum values to names, initializing tries
for classifying UTF-8 characters, etc.).
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,787
Messages
2,569,629
Members
45,332
Latest member
LeesaButts

Latest Threads

Top