Object factory and Intel compiler

R

Ryan Mitchley

Hi all

I have code for an object factory, heavily based on an article by Jim Hyslop
(although I've made minor modifications). The factory was working fine using
g++, but since switching to the Intel compiler it has stopped working. I
think the singleton pattern static instance thing may be at the root of the
problem, but I'm not sure. I had to add calls to instance() in regCreateFn,
which gets the behaviour more in line with what I was expecting during
static initialization (the objects all register with the same registry). I
have tried playing with the MS compatibility options of the Intel compiler,
with no success.

The problem occurs when I make a call to manufacture a given object: a
totally different instance of the factory and registry are used (I have
checked this by printing out pointers).

A typical call to the manufacture method would be:

shared_ptr<CAngle3D> newB = boost::shared_polymorphic_cast<CAngle3D>
(objectFactory<CBase>::instance().manufacture("CAngle3D"));

This fails, since the objectFactory<CBase>::instance() returns a valid
factory, but one with no items in the map of create functions (i.e. it is a
different one to that used when registering the create functions in the
static initalizers).

In this example, the first section of the header for CAngle3D is as follows:
class CAngle3D : public CBase
{
public:
// Tell the Object Factory about this class
//FACTORY_DECLARE_CLASS(CAngle3D
static registerInFactory<CBase, CAngle3D> regClass;

and the following is placed into the cpp for CAngle3D:
// Register class with Object Factory
//FACTORY_REGISTER_CLASS(CAngle3D)
registerInFactory<CBase, CAngle3D> CAngle3D::regClass("CAngle3D");

The complete CFactory.h listing is pasted at the end of this email. You will
notice that there is a large amount of debugging info being output.

The output of my program, when it starts up, is as follows: (Notice
especially the memory addresses, that change when the program tries to
manufacture a CAngle3D object)

$ ./TestMathCore
Constructor: objectFactory for class CBase
Registering create function for "CAngle3D"
Registry size = 1(0046FBD0)
Registering clone function for "CAngle3D"
registerInFactory: "CAngle3D"
Constructor: objectFactory for class CBase
Registering create function for "CFrequency"
Registry size = 2(0046FBD0)
Registering clone function for "CFrequency"
registerInFactory: "CFrequency"
Constructor: objectFactory for class CBase
Registering create function for "CList"
Registry size = 3(0046FBD0)
Registering clone function for "CList"
registerInFactory: "CList"
Constructor: objectFactory for class CBase
Registering create function for "CSortableList"
Registry size = 4(0046FBD0)
Registering clone function for "CSortableList"
registerInFactory: "CSortableList"
Constructor: objectFactory for class CBase
Registering create function for "CCollection"
Registry size = 5(0046FBD0)
Registering clone function for "CCollection"
registerInFactory: "CCollection"
Constructor: objectFactory for class CBase
Registering create function for "CAngle"
Registry size = 6(0046FBD0)
Registering clone function for "CAngle"
registerInFactory: "CAngle"
Min resolution = 1, Max resolution = 1000000
Timer setting successful.
The system clock interval is 10 ms.
Adjustment disabled = 1
MathCore Test App
-----------------

CAngle3D
Constructor: objectFactory for class CBase
Registry size = 0(0046FB04)
Contents of Object Factory warehouse:

Trying to manufacture "CAngle3D"
Object Factory 0046FB04: 0 items
objectFactory: Don't know how to manufacture that object type.
Ensure that FACTORY_REGISTER_CLASS is used.

I would *really* appreciate some help! Sorry for the hugely long post . . .

Ryan


// CFactory.h
// object abstract factory template.
// Copyright 2001, Jim Hyslop.
// This file may be freely used, modified and distributed, provided that
// the accompanying copyright notice remains intact.
//
// The object abstract factory template is an implementation of the
// Abstract Class Factory pattern, as a template (see "Design Patterns:
// Elements of Reusable Object-Oriented Software", E. Gamma, R. Helm,
// R. Johnson, J. Vlissides, Addison Wesley [1995] )
//
// To use the template, you need to provide a base class and (optionally)
// a key class. The base class must provide a unique identifier
//
// The key class must be able to be used as a key in a std::map, i.e. it
must
// - implement copy and assignment semantics
// - provide bool operator< () const;
// Default is std::string.
//
// Steps to using the factory:
// 1 - Create the base class and its derivatives
// 2 - Register each class in the factory by instantiating a
// registerInFactory<> template class - do this in one file only (the
// class implementation file is the perfect place for this)
// 3 - create the object by calling create() and passing it the same
// value used when you instantiated the registerInFactory object.
// For example:
// base header:
// class Base { /* whatever (don't forget the virtual dtor! */ };
//
// base implementation:
// registerInFactory<Base, Base, std::string> registerBase("Base");
//
// derived header:
// class Derived : public Base { /* whatever */ };
//
// derived implementation:
// registerInFactory<Base, Derived, std::string> registerDer("Derived");
//
// code that instantiates the classes:
// std::auto_ptr<Base> newBase =
objectFactory<Base>::instance().create("Base");
// std::auto_ptr<Base> newDerived =
objectFactory<Base>::instance().create("Derived");
//
// New derivatives can be added without affecting the existing code.

#ifndef FACTORY_HEADER_DEFINED
#define FACTORY_HEADER_DEFINED

#include <boost/shared_ptr.hpp>

using boost::shared_ptr;

//#pragma warning(disable: 4275)
//#pragma warning(disable: 4786)

// Macro to simplify registration with the Object Factory and creation of a
Clone function
// To be used in the class declaration in the header file (.h) for the new
class
#define FACTORY_DECLARE_CLASS(_cNewClass) \
static registerInFactory<CBase,_cNewClass>regClass;\
virtual shared_ptr<CBase>Clone()\
{return shared_ptr<CBase>(new _cNewClass(*this));}

// Macro to simplify implementation of the registration function
// To be used in the class implementation (.cpp) file for the new class
#define FACTORY_REGISTER_CLASS(_cNewClass) \
registerInFactory<CBase, _cNewClass> _cNewClass::regClass(#_cNewClass);

// The abstract factory itself.
// Implemented using the Singleton pattern

template <class manufacturedObj>
class objectFactory
{
public:
// a BASE_CREATE_FN is a function that takes no parameters
// and returns a pointer to a manufactuedObj. Note that
// we use no parameters, but you could add them
// easily enough to allow overloaded ctors, e.g.:
typedef manufacturedObj* (*BASE_CREATE_FN)();

typedef manufacturedObj* (*BASE_CLONE_FN)(manufacturedObj*);

int iTest;
// FN_REGISTRY is the registry of all the BASE_CREATE_FN
// pointers registered. Functions are registered using the
// regCreateFn member function (see below).
typedef map<string, BASE_CREATE_FN> FN_REGISTRY;
FN_REGISTRY registry;

typedef map<string, BASE_CLONE_FN> FN_CLONE_REGISTRY;
FN_CLONE_REGISTRY CloneRegistry;

// Singleton implementation - private ctor & copying, with
// no implementation on the copying.
objectFactory();
objectFactory(const objectFactory&); // Not implemented
objectFactory &operator=(const objectFactory&); // Not implemented
public:
// Singleton access.
static objectFactory &instance();

// Method to register the class ID key, and a pointer to
// the function that creates the class.
void regCreateFn(const string & strClassID, BASE_CREATE_FN);

// Method to register the class ID key, and a pointer to
// the function that clones
void regCloneFn(const string & strClassID, BASE_CLONE_FN);

// Create a new class of the type specified by className.
// manufacturedObj* manufacture(const std::string & className) const;
shared_ptr<manufacturedObj> manufacture(const std::string & className)
const;

shared_ptr<manufacturedObj> clone(shared_ptr<manufacturedObj> pToClone)
const;

// Create a new class of the type specified by className.
shared_ptr<manufacturedObj> assemble(list<string> &is,
list<string>::const_iterator& i) const;
};

////////////////////////////////////////////////////////////////////////
// Implementation details.

template <class manufacturedObj>
objectFactory<manufacturedObj>::eek:bjectFactory()
{
cout << "Constructor: objectFactory for " << typeid(manufacturedObj).name()
<< endl;
}

template <class manufacturedObj>
objectFactory<manufacturedObj> & objectFactory<manufacturedObj>::instance()
{
// Note that this is not thread-safe!
static objectFactory theInstance;
return theInstance;
}

// Register the creation function. This simply associates the classIDKey
// with the function used to create the class. The return value is a dummy
// value, which is used to allow static initialization of the registry.
// See example implementations in base.cpp and derived.cpp
template <class manufacturedObj>
void objectFactory<manufacturedObj>::regCreateFn(const string & strClassID,
BASE_CREATE_FN func)
{
cout << "Registering create function for \"" << strClassID << "\"" << endl;
instance().registry[strClassID] = func;
cout << "Registry size = " << instance().registry.size() << "(" <<
&(instance().registry) << ")" << endl;
}

template <class manufacturedObj>
void objectFactory<manufacturedObj>::regCloneFn(const string & strClassID,
BASE_CLONE_FN func)
{
cout << "Registering clone function for \"" << strClassID << "\"" << endl;
instance().CloneRegistry[strClassID]=func;
}

template <class manufacturedObj>
shared_ptr<manufacturedObj>
objectFactory<manufacturedObj>::manufacture(const std::string & className)
const
{
cout << "Registry size = " << instance().registry.size() << "(" <<
&(instance().registry) << ")" << endl;
shared_ptr<manufacturedObj> manufactured;
cout << "Contents of Object Factory warehouse:" << endl;
typename FN_REGISTRY::const_iterator regIter = instance().registry.begin();
while (regIter != instance().registry.end())
cout << regIter->first << endl;
cout << endl;

typename FN_REGISTRY::const_iterator regEntry =
instance().registry.find(className);
cout << "Trying to manufacture \"" << className << "\"" << endl;
cout << "Object Factory " << &(instance().registry) << ": " <<
instance().registry.size() << " items" << endl;
if (regEntry != instance().registry.end())
manufactured.reset((*regEntry).second());
else
cout << "objectFactory: Don't know how to manufacture that object
type.\nEnsure that FACTORY_REGISTER_CLASS is used." << endl;
return manufactured;
}

// Clone an object using the class name of the cloned object
template <class manufacturedObj>
shared_ptr<manufacturedObj>
objectFactory<manufacturedObj>::clone(shared_ptr<manufacturedObj> pToClone)
const
{
cout << "clone(shared_ptr<manufacturedObj> pToClone) const" << endl;
shared_ptr<manufacturedObj> manufactured;
typename FN_CLONE_REGISTRY::const_iterator
regEntry=CloneRegistry.find(pToClone->ClassName());
if (regEntry != CloneRegistry.end())
manufactured =
shared_ptr<manufacturedObj>((*regEntry).second(pToClone.get()));
else
cout << "objectFactory: Don't know how to clone that object type.\nEnsure
that FACTORY_REGISTER_CLASS is used." << endl;
return manufactured;
}

// Assemble a new manufacturedObj using the list of string tokens and
iterator supplied
template <class manufacturedObj>
shared_ptr<manufacturedObj>
objectFactory<manufacturedObj>::assemble(list<std::string> &is,
list<string>::const_iterator& i) const
{
cout << "assemble" << endl;
string strObjType, strObjName;
list<string>::const_iterator start = i;
while ((i != is.end()) && ((*i).find("XSIL") == string::npos)) i++;
// Read back the object name, if it is present
while ((i != is.end()) && ((*i).find("Type") == string::npos)) i++;
while ((i != is.end()) && ((*i).find("=") == string::npos)) i++;
i++; strObjType = *i;
shared_ptr<manufacturedObj> ret;
typename FN_REGISTRY::const_iterator regEntry=registry.find(strObjType);
if (regEntry != registry.end())
ret.reset((*regEntry).second());
i = start;
ret->XML_Read(is, i);
return ret;
}

// ************************************************************
// Helper template to make registration painless and simple
// ************************************************************
template <class ancestorType,
class manufacturedObj>
class registerInFactory
{
public:
static ancestorType* createInstance()
{
cout << "createInstance()" << endl;
return (ancestorType*)(new manufacturedObj);
}
static ancestorType* cloneInstance(ancestorType * pObjToClone)
{
cout << "cloneInstance(ancestorType * pObjToClone)" << endl;
manufacturedObj * pDerivedToClone = new manufacturedObj;
*pDerivedToClone = *((manufacturedObj*)pObjToClone);
// pDerivedToClone = (manufacturedObj*) pObjToClone;
// cout << "Cloning " << pObjToClone->GetID() << endl;
// return (ancestorType*)(new
manufacturedObj(*((manufacturedObj*)pObjToClone)));
return (ancestorType*)pDerivedToClone;
}
registerInFactory(const string id)
{
m_strClassName.assign(id);
objectFactory<ancestorType>::instance().regCreateFn(m_strClassName,
createInstance);
objectFactory<ancestorType>::instance().regCloneFn(m_strClassName,
cloneInstance);
cout << "registerInFactory: \"" << m_strClassName << "\"" << endl;
}
protected:
string m_strClassName;
};

#endif
 
G

Gianni Mariani

Ryan said:
Hi all

I have code for an object factory, heavily based on an article by Jim Hyslop
(although I've made minor modifications). The factory was working fine using
g++, but since switching to the Intel compiler it has stopped working. I
think the singleton pattern static instance thing may be at the root of the
problem, but I'm not sure. I had to add calls to instance() in regCreateFn,
which gets the behaviour more in line with what I was expecting during
static initialization (the objects all register with the same registry). I
have tried playing with the MS compatibility options of the Intel compiler,
with no success.

I have no idea, but creating singleton instances of factory registries
is a tricky thing to do when messing with DLL's and template instances
all around.

May I suggest an alternative ? I released the Austria C++ library a few
weeks ago that does implement a generic factory mechanism and it does
deal with DLL/DSO hell. I have not tested it with the intel compiler.

This is an example taken from the Austria test case.


class Interface
{

public:

virtual const char * Thingy() = 0;
virtual const char * Thingy1() = 0;
virtual const char * Thingy2() = 0;

bool * mark_del;

Interface()
: mark_del( 0 )
{
}

virtual ~Interface()
{
if ( mark_del )
{
* mark_del = true;
}
}

void MarkPtr( bool * i_mark_del )
{
mark_del = i_mark_del;
}

virtual int ContructParamCount() = 0;

};


class Implementor
: public Interface
{

public:

virtual const char * Thingy()
{
return m_str;
}

virtual const char * Thingy1()
{
return m_str2;
}

virtual const char * Thingy2()
{
return m_str3;
}

Implementor( const char * str, const char * str2, const char * str3 )
: m_str( str ),
m_str2( str2 ),
m_str3( str3 ),
contruction_parameter_count( 3 )
{
}

Implementor( const char * str, const char * str2 )
: m_str( str ),
m_str2( str2 ),
contruction_parameter_count( 2 )
{
}

Implementor( const char * str )
: m_str( str ),
contruction_parameter_count( 1 )
{
}

Implementor()
: m_str( "Empty" ),
contruction_parameter_count( 0 )
{
}

virtual int ContructParamCount()
{
return contruction_parameter_count;
}

int contruction_parameter_count;

const char * m_str;
const char * m_str2;
const char * m_str3;
};


AT_MakeFactory1P( "ImplementorKEY", Implementor, Interface, DKy, const
char * );
AT_MakeFactory2P( "ImplementorKEY", Implementor, Interface, DKy, const
char *, const char * );
AT_MakeFactory3P( "ImplementorKEY", Implementor, Interface, DKy, const
char *, const char *, const char * );

AT_MakeFactory0P( "ImplementorKEY", Implementor, Interface, DKy );


http://austria.sourceforge.net
http://austria.sourceforge.net/dox/html/at__factory_8h-source.html

The build system may not work for you, however the Austria build is
fairly easy to configure.
 
M

Marco Manfredini

Ryan said:
Hi all

I have code for an object factory, heavily based on an article by Jim
Hyslop (although I've made minor modifications). The factory was
working fine using g++, but since switching to the Intel compiler it
has stopped working. I think the singleton pattern static instance
thing may be at the root of the problem,

This could be a platform dependent problem. Can give more details?

=> What's the OS?
=> are the factories registered/used in a dll/dso?
=> if so, how do link to the dll/dso? Directly, through an import
library or do you use something like LoadLibrary/dlopen?
=> What version did the g++ compiler have that used to work and what
version of Intel's compiler are you trying to use now?

Marco
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top