Templates & abstraction question

A

AzizMandar

There is probably a better way to do this and if so I'm just as happy
to see that way.


I have a program where I have factories that each create various
objects abstracted from a base class. The Factories all have a base
class as well for the objects to point back to.

So I have

class CBFactory{
public:
CBFactory();
~CBFactory();
}

class CBObject{
CBFactory* m_home;
public:
CBObject();
~CBObject();
}

class SillyObject:public CBObject{
//Code that makes it unique
public:
SillyObject(CBFactory*); //Set the m_home on creation
~SillyObject();

}

class SillyFactory:public CBFactory,public singleton{
public:
SillyFactory();
~SillyFactory();
CBObject* MakeObj(){
SillyObject* NewObj = new SillyObject(this);
return NewObj;
}
}


Now Singleton just adds my singleton elements (It works a little
diffrant but I included it to show the multiple inheritance I use)

Right now I have a lot of member functions like MakeObj() and they are
so similar in each factory that it would be nice to template them. The
problem is I don't know if there is a way to let the template know that
the dependant class type (SillyObject in this case) is derived from
CBObjects since that is what I am returning a pointer to. I could
probably explicit cast it and tell the compiler to ignore the casting
but I figure there could be a more type safe way to do it.
 
A

amparikh

AzizMandar said:
There is probably a better way to do this and if so I'm just as happy
to see that way.


I have a program where I have factories that each create various
objects abstracted from a base class. The Factories all have a base
class as well for the objects to point back to.

So I have

class CBFactory{
public:
CBFactory();
~CBFactory();
}

class CBObject{
CBFactory* m_home;
public:
CBObject();
~CBObject();
}

class SillyObject:public CBObject{
//Code that makes it unique
public:
SillyObject(CBFactory*); //Set the m_home on creation
~SillyObject();

}

class SillyFactory:public CBFactory,public singleton{
public:
SillyFactory();
~SillyFactory();
CBObject* MakeObj(){
SillyObject* NewObj = new SillyObject(this);
return NewObj;
}
}


Now Singleton just adds my singleton elements (It works a little
diffrant but I included it to show the multiple inheritance I use)

Right now I have a lot of member functions like MakeObj() and they are
so similar in each factory that it would be nice to template them. The
problem is I don't know if there is a way to let the template know that
the dependant class type (SillyObject in this case) is derived from
CBObjects since that is what I am returning a pointer to. I could
probably explicit cast it and tell the compiler to ignore the casting
but I figure there could be a more type safe way to do it.

so what's the problem in doing the following ?

template<typename T>
T* SillyFactory::MakeObj(){
T* NewObj = new T(this);
return NewObj;
}

Make the MakeObj function to be a static function and then you can use.

SillyObject* p = SillyFactory::Create<SillyObject>();
 
M

mlimber

AzizMandar said:
There is probably a better way to do this and if so I'm just as happy
to see that way.


I have a program where I have factories that each create various
objects abstracted from a base class. The Factories all have a base
class as well for the objects to point back to.

So I have

class CBFactory{
public:
CBFactory();
~CBFactory();
}

class CBObject{
CBFactory* m_home;
public:
CBObject();
~CBObject();
}

class SillyObject:public CBObject{
//Code that makes it unique
public:
SillyObject(CBFactory*); //Set the m_home on creation
~SillyObject();

}

class SillyFactory:public CBFactory,public singleton{
public:
SillyFactory();
~SillyFactory();
CBObject* MakeObj(){
SillyObject* NewObj = new SillyObject(this);
return NewObj;
}
}


Now Singleton just adds my singleton elements (It works a little
diffrant but I included it to show the multiple inheritance I use)

Right now I have a lot of member functions like MakeObj() and they are
so similar in each factory that it would be nice to template them. The
problem is I don't know if there is a way to let the template know that
the dependant class type (SillyObject in this case) is derived from
CBObjects since that is what I am returning a pointer to. I could
probably explicit cast it and tell the compiler to ignore the casting
but I figure there could be a more type safe way to do it.

Check out the factory and singleton classes described in _Modern C++
Design_ by Alexandrescu. The code can be downloaded for free from:

http://sourceforge.net/projects/loki-lib/

You can see an example of the Singleton usage here:

http://groups.google.com/group/comp.lang.c++/msg/3eadedc5b34ee23d

The factory allows you to register a creation function and then create
using some ID. It's quite clever and quite useful!

Cheers! --M
 
A

AzizMandar

so what's the problem in doing the following ?
template<typename T>
T* SillyFactory::MakeObj(){
T* NewObj = new T(this);
return NewObj;

}Make the MakeObj function to be a static function and then you can use.

SillyObject* p = SillyFactory::Create<SillyObject>();

The MakeObj() is no longer returning type CBObject* it is now
returning the type T* (so SillyObject if I did
SillyFactory<SillyObject>)

When I would go to call CBOFactory::NewObj() from SillyFactory the
return type would not match. I don't even think it would compile
because the arguments match and return types don't.

In my code I don't know that SIllyFactory or SillyObject will be used
(I want to make what ever ones I need without changing the core
program) So I can only deal with CBObject and CBOFactory. I can just
explicitly override the function MakeObj() and make it virtual in
CBOFactory but I am copying and pasting code every time and only
changing the inherited CBObject class. I was hoping for a template
solution but the reliance on the type T to be inherited from CBObject
is causing a bit of a problem. Of course I guess I can explicit cast T
to CBObject for the return type but if it was ever of a class not
inherited from CBObject that would be REALLY bad (hence why that
process would be 'EVIL')
 
A

AzizMandar

Check out the factory and singleton classes described in _Modern C++
Design_ by Alexandrescu. The code can be downloaded for free from:

http://sourceforge.net/projects/loki-lib/

You can see an example of the Singleton usage here:

http://groups.google.com/group/comp.lang.c++/msg/3eadedc5b34ee23d

The factory allows you to register a creation function and then create
using some ID. It's quite clever and quite useful!

Cheers! --M

Thanks. I'll look into that. The Singleton is very similar to one I
use so I'm happy to see I'm at least doing that right. I have read
things similar to what you describe about the factory and was trying to
avoid that in my program design (ID and lookup) but it is safe and it
can be templated much easier. I'm just worried about performance of an
ID lookup routine rather than a straight pointer call. I'm not worried
about losing pointer or trying to call a deleted pointer (My garbage
class is working well for that) but I will look over the information
and maybe redesign some of my code.

Thanks again
 
S

Salt_Peter

AzizMandar said:
There is probably a better way to do this and if so I'm just as happy
to see that way.


I have a program where I have factories that each create various
objects abstracted from a base class. The Factories all have a base
class as well for the objects to point back to.

So I have

class CBFactory{
public:
CBFactory();
~CBFactory();
}

class CBObject{
CBFactory* m_home;
public:
CBObject();
~CBObject();
}

class SillyObject:public CBObject{
//Code that makes it unique
public:
SillyObject(CBFactory*); //Set the m_home on creation
~SillyObject();

}

class SillyFactory:public CBFactory,public singleton{
public:
SillyFactory();
~SillyFactory();
CBObject* MakeObj(){
SillyObject* NewObj = new SillyObject(this);
return NewObj;
}
}

Your class definitions need semicolons. MakeObj() should be static. Any
base class you allocate via new/delete needs a vitual d~tor (MakeObj()
is returning a base ptr). The goal is to isolate the singleton's
interface so only the static creator function is accessible. You should
prefer smart pointers over new/delete.
 
N

Nindi73

You are more than welcome to look at my factory code, it makes heavy
use of Boost ( which is a good thing). There are actualy 3 factories.
One returning new objects derived from a base class, and the base class
must have a typelist to identify the constructor to be used by the
factory.
The second returns a reference to a shared, static object, again the
base class must have a typelist identifying the signature of the
constructor.

The third Factory produces derived objects of a given base class, but
queries the derived objects for a typelist so it can work out which
consructor to call to produce the object.

I'll first pate in the main.cpp, it shoes example code using the
factory, followed by the factory code.


-------------------------------------------------------------------------------------------------------------------------------
<main.cpp>
#include<vector>
#include<iostream>
#include"Factory.h"
#include"Phactory.h"

using namespace std;

struct MyClassVoid {
typedef boost::mpl::list<> Constructor_TypeList;
MyClassVoid(){}
virtual void f()const=0;
virtual ~MyClassVoid(){};
};
struct MyClassVoidDerived :public MyClassVoid {
MyClassVoidDerived(){}
virtual void f()const{cout << " I am in MyClassVoidDerived \n";}
virtual ~MyClassVoidDerived(){};
};

namespace {
Registra<MyClassVoid,MyClassVoidDerived>
theRegistraVoid(std::string("MyClassVoidDerived"));
}

struct MyClassDouble {
typedef boost::mpl::list<double> Constructor_TypeList;
typedef boost::mpl::list<double> variant_type_list;
MyClassDouble(double){}
virtual void f(){cout << " I am in MyClassDouble \n";}
virtual ~MyClassDouble(){};
};
struct MyClassDoubleDerived :public MyClassDouble {
typedef boost::mpl::list<double> constructor_signature_typelist;
MyClassDoubleDerived(double x):MyClassDouble(x){}
virtual void f(){cout << " I am in MyClassDoubleDerived \n";}
virtual ~MyClassDoubleDerived(){};
};
namespace {
Registra<MyClassDouble,MyClassDouble>
theRegistra(std::string("MyClassDouble"));
Registra<MyClassDouble,MyClassDoubleDerived>
theRegistraD(std::string("MyClassDoubleDerived"));
}

struct MyClassMultiple {
typedef
boost::mpl::list said:
Constructor_TypeList;
MyClassMultiple(std::string,std::vector<double>,double,MyClassDoubleDerived){}
virtual void f(){cout << " I am in MyClassMultiple \n";}
virtual ~MyClassMultiple(){};
};

namespace {
Registra<MyClassMultiple,MyClassMultiple>
theRegistraM(std::string("MyClassMultiple"));
StaticRegistra<MyClassMultiple,MyClassMultiple>
theStaticRegistraM(std::string("MyClassMultiple"),std::string("String"),std::vector<double>(2),1.0,MyClassDoubleDerived(1.0));
}

struct Dummy{};

struct Base {
typedef boost::mpl::list<double,int,unsigned
long,std::string,std::vector<double>,float,Dummy> variant_type_list;
Base(){}
virtual void f()const=0;
virtual ~Base(){};
};
struct Derived :public Base {
typedef boost::mpl::list<double,int,std::string,std::vector<double> >
constructor_signature_typelist;
Derived(double x_,int n_,const std::string &s_,const
std::vector<double>&v_):
x(x_),n(n_),s(s_),v(v_){}
virtual void f()const{
cout << "My Double is " << x << "\n"
<< "My int is " << n << "\n"
<< "My String is " << s << "\n"
<< "My Vector Size is " << v.size() << "\n";
}
private:
double x;
int n;
std::string s;
std::vector<double> v;
};
namespace {
const char * theVarNames[]={"Double","Int","String","Vector"};
PhRegistra<Base,Derived>
thePhRegistra(std::string("DerivedObject"),PARAM_NAMES(theVarNames));
}


int main() {

boost::shared_ptr said:
::instance().RetrieveObject("MyClassVoidDerived"));
thePtrVoid->f();


boost::shared_ptr said:
::instance().RetrieveObject("MyClassDoubleDerived",1.0));
thePtrDoubleDerived->f();

boost::shared_ptr said:
::instance().RetrieveObject("MyClassDouble",1.0));
thePtrDouble->f();

boost::shared_ptr said:
::instance().RetrieveObject("MyClassMultiple",std::string("String"),std::vector<double>(2),1.0,MyClassDoubleDerived(1.0)));
thePtrM->f();

boost::shared_ptr said:
::instance().RetrieveObject("MyClassMultiple"));
thePtrSM->f();

std::map<std::string,typename boost::make_variant_over<typename
Base::variant_type_list::type>::type> theMap;

theMap["Double"]=1.0;
theMap["Int"]=1;
theMap["String"]="hello";
theMap["Vector"]=std::vector<double>(10);

boost::shared_ptr said:
::instance().RetrieveObject("DerivedObject",theMap));
thePtrPhM->f();


}
-------------------------------------------------------------------------------------------------------------------------------------
The Factory code

---------------------------------------------------------------------------
<Singleton.h>
#ifndef SINGLETON_HEADER_GUARD
#define SINGLETON_HEADER_GUARD
#include <boost/utility.hpp>

template<class Object>
struct Singleton :boost::noncopyable{
public:

static Object & instance(){static Object theObject;return theObject;}
virtual ~Singleton(){}

};
#endif
--------------------------------------------------------------------------------------

<Factory.h>
#ifndef FACTORY_HEADER_GUARD
#define FACTORY_HEADER_GUARD

#include<string>
#include<map>

#include"Singleton.h"

#include<boost/shared_ptr.hpp>
#include<boost/mpl/size.hpp>
#include<boost/mpl/list.hpp>
#include<boost/mpl/at.hpp>

#include<boost/preprocessor/repetition.hpp>
#include<boost/preprocessor/arithmetic/sub.hpp>

#ifndef MAX_FACTORIES
#define MAX_FACTORIES 10
#endif

// One Registration object per class in a hireachy, this is the
template blue print for the base classes of
// the registration objects

template
<
class BaseClass,
typename Key = std::string,
unsigned long = boost::mpl::size<typename
BaseClass::Constructor_TypeList>::valuestruct RegistraBase {};

// This is the template blue print for the concrete classes of
// the registration objects

template
<
class BaseClass,
class Derived,
typename Key = std::string,
unsigned long n = boost::mpl::size<typename
BaseClass::Constructor_TypeList>::valuestruct Registra :public RegistraBase<BaseClass,Key,n>{};

// This is the factory template blue print of the Factory class

template
<
class BaseClass,
typename Key = std::string ,
unsigned long n = boost::mpl::size<typename
BaseClass::Constructor_TypeList>::valueclass Factory {};

//Helper Macros
#define PARAM(z,Nb,data) typename boost::mpl::at_c<typename
BaseClass::Constructor_TypeList,Nb>::type P##Nb

#define REGISTRABASE(z,Nb,data) \
template<class BaseClass,typename Key> struct
RegistraBase<BaseClass,Key,Nb> { \
virtual boost::shared_ptr<BaseClass>
RetrieveObject(BOOST_PP_ENUM(Nb,PARAM,~))const=0; \
};

BOOST_PP_REPEAT(MAX_FACTORIES,REGISTRABASE,~)


#define FACTORY(z,Nb,data) \
template \
< \
class BaseClass, \
typename Key \
class Factory<BaseClass,Key,Nb> { \
public: \
bool Register(const Key & theKey,RegistraBase<BaseClass,Key,Nb>
*theRegistra){ \
return
theInnerMap.insert(InnerMap::value_type(theKey,theRegistra)).second; \
} \
\
boost::shared_ptr<BaseClass> RetrieveObject(const Key &id
BOOST_PP_COMMA_IF(Nb) BOOST_PP_ENUM(Nb,PARAM,~))const { \
InnerMapIterator theIterator (theInnerMap.find(id)); \
if(theIterator==theInnerMap.end())return
boost::shared_ptr<BaseClass>(static_cast<BaseClass *>(0)); \
return theIterator->second->RetrieveObject(
BOOST_PP_ENUM_PARAMS(Nb,P)); \
} \
private: \
typedef typename std::map<Key,RegistraBase<BaseClass,Key,Nb> *>
InnerMap; \
typedef typename std::map<Key,RegistraBase<BaseClass,Key,Nb>
*>::const_iterator InnerMapIterator; \
std::map<Key,RegistraBase<BaseClass,Key,Nb> *>
theInnerMap; \
};

BOOST_PP_REPEAT(MAX_FACTORIES,FACTORY,~)

// The macro defintions of the Registration Objects

#define REGISTRA(z,Nb,data) \
template<class BaseClass,class Derived,typename
Key> \
struct Registra<BaseClass,Derived,Key,Nb>:public
RegistraBase said:
::instance().Register(theKey,this);} \
boost::shared_ptr<BaseClass>
RetrieveObject(BOOST_PP_ENUM(Nb,PARAM,~))const \
{return boost::shared_ptr<BaseClass>(new Derived(
BOOST_PP_ENUM_PARAMS(Nb,P)));} \
virtual ~Registra(){} \
};

BOOST_PP_REPEAT(MAX_FACTORIES,REGISTRA,~)

#undef MAX_FACTORIES
#undef PARAM
#undef REGISTRA
#undef REGISTRABASE
#undef FACTORY

#include"StaticFactory.h"

#endif
-----------------------------------------------------------------------------------------------------------------------------


<StaticFactory.h>
#ifndef STATIC_FACTORY_HEADER_GUARD
#define STATIC_FACTORY_HEADER_GUARD
#include<string>
#include<map>

#include"Singleton.h"

#include<boost/shared_ptr.hpp>
#include<boost/mpl/size.hpp>
#include<boost/mpl/list.hpp>
#include<boost/mpl/at.hpp>

#include<boost/preprocessor/repetition.hpp>
#include<boost/preprocessor/arithmetic/sub.hpp>

#ifndef MAX_STATIC_FACTORIES
#define MAX_STATIC_FACTORIES 10
#endif


template
<
class BaseClass,
typename Key = std::stringstruct StaticRegistraBase {
virtual boost::shared_ptr<BaseClass> RetrieveObject()const=0;
};

template
<
class BaseClass,
class Derived,
typename Key = std::string,
unsigned long n = boost::mpl::size<typename
BaseClass::Constructor_TypeList>::valuestruct StaticRegistra :public StaticRegistraBase<BaseClass,Key>{};

template
<
class BaseClass,
typename Key=std::stringclass StaticFactory {
public:
bool Register(const Key & theKey,StaticRegistraBase<BaseClass,Key>
*theRegistra){
return
theInnerMap.insert(InnerMap::value_type(theKey,theRegistra)).second;
}
boost::shared_ptr<BaseClass> RetrieveObject(const Key &id )const {
InnerMapIterator theIterator (theInnerMap.find(id));
if(theIterator==theInnerMap.end())return
boost::shared_ptr<BaseClass>(static_cast<BaseClass *>(0));
return theIterator->second->RetrieveObject();
}

private:
typedef typename std::map<Key,StaticRegistraBase<BaseClass,Key> *>
InnerMap;
typedef typename std::map<Key,StaticRegistraBase<BaseClass,Key>
*>::const_iterator InnerMapIterator;
std::map<Key,StaticRegistraBase<BaseClass,Key> *> theInnerMap;

};

//Helper Macros
#define PARAM(z,Nb,data) typename boost::mpl::at_c<typename
BaseClass::Constructor_TypeList,Nb>::type P##Nb

#define STATICREGISTRA(z,Nb,data) template<class BaseClass,class
Derived,typename Key> \
struct StaticRegistra<BaseClass,Derived,Key, Nb >:public
StaticRegistraBase<BaseClass,Key> \
{ \
StaticRegistra(const Key & theKey BOOST_PP_COMMA_IF(Nb)
BOOST_PP_ENUM(Nb,PARAM,~)): \
theObjectPtr(new Derived(BOOST_PP_ENUM_PARAMS(Nb,P))) \
{ \
Singleton said:
::instance().Register(theKey,this); \
} \
boost::shared_ptr<BaseClass> RetrieveObject()const{return
theObjectPtr;} \
virtual ~StaticRegistra(){} \
private: \
boost::shared_ptr<BaseClass> theObjectPtr; \
};

BOOST_PP_REPEAT(MAX_STATIC_FACTORIES,STATICREGISTRA,~)

#undef MAX_STATIC_FACTORIES
#undef PARAM
#undef STATICREGISTRA

#endif
--------------------------------------------------------------------------------------------------------------


<Phactory.h>
#ifndef PHACTORY_HEADER_GUARD
#define PHACTORY_HEADER_GUARD


#include<string>
#include<map>
#include<cassert>

#include<boost/shared_ptr.hpp>
#include<boost/variant.hpp>
#include<boost/utility.hpp>
#include<boost/static_assert.hpp>

#include<boost/mpl/size.hpp>
#include<boost/mpl/list.hpp>
#include<boost/mpl/at.hpp>

#include<boost/preprocessor/repetition.hpp>


#ifndef MAX_PHACTORIES
#define MAX_PHACTORIES 10
#endif



template<unsigned long i>
struct unsigned_long {enum {value=i};};

template<class BaseClass>
struct PhRegistraBase {
typedef typename boost::make_variant_over<typename
BaseClass::variant_type_list::type>::type variant;
typedef std::map<std::string,variant> ParameterMap;

virtual boost::shared_ptr<BaseClass> RetrieveObject(const std::string
&theKey,const ParameterMap& theMap )const=0;
};

template<class BaseClass>
class Phactory {
public:
typedef typename boost::make_variant_over<typename
BaseClass::variant_type_list::type>::type variant;
typedef std::map<std::string,variant> ParameterMap;
bool Register(const std::string &theKey, const
PhRegistraBase<BaseClass>* theRegistra){
return
theInnerMap.insert(InnerMap::value_type(theKey,theRegistra)).second;
}
boost::shared_ptr<BaseClass> RetrieveObject(const std::string &id,
const ParameterMap & theMap)const {
InnerMapIterator theIterator (theInnerMap.find(id));
if(theIterator==theInnerMap.end())return
boost::shared_ptr<BaseClass>(static_cast<BaseClass *>(0));
return theIterator->second->RetrieveObject(id,theMap);
}


private:
typedef typename std::map<std::string,const PhRegistraBase<BaseClass>
*> InnerMap;
typedef typename std::map<std::string,const PhRegistraBase<BaseClass>
*>::const_iterator InnerMapIterator;
std::map<std::string,const PhRegistraBase<BaseClass> *> theInnerMap;
};


#define PARAM(z,Nb,data) boost::get<const boost::mpl::at_c<signature,
Nb >::type &>(theMap.find(theVarNames[Nb])->second)

#define INNER_RETRIVE_OBJECT(z,Nb,data) \
template said:
(const std::string &theKey,const ParameterMap& theMap) \
const{ \
CheckMap(theMap); \
return boost::shared_ptr<BaseClass>(new DerivedClass(
BOOST_PP_ENUM(Nb,PARAM,~) ) \
); \
}



template
<
class BaseClass,
class DerivedClassclass PhRegistra:public PhRegistraBase<BaseClass>{
public:
typedef typename DerivedClass::constructor_signature_typelist
signature;
typedef typename boost::make_variant_over<typename
BaseClass::variant_type_list::type>::type variant;
typedef std::map<std::string,variant> ParameterMap;

enum {ssize = boost::mpl::size<signature>::value};

template<unsigned long i>
PhRegistra(const std::string &theKey, const char
*theVariableNames[],const unsigned_long<i> *p=0)
{
BOOST_STATIC_ASSERT(i==ssize); // Must have ONE variable name for
each paramter of the constructor
for(unsigned long i(0);i<ssize;++i)
theVarNames=std::string(theVariableNames);
Singleton<Phactory<BaseClass> >::instance().Register(theKey,this);
}

boost::shared_ptr<BaseClass> RetrieveObject(const std::string
&theKey,const ParameterMap& theMap)const{
return InnerRetrieveObject<ssize>(theKey,theMap);
}
template<int i>
boost::shared_ptr<BaseClass> InnerRetrieveObject(const std::string
&theKey,const ParameterMap&)const;

BOOST_PP_REPEAT(MAX_PHACTORIES,INNER_RETRIVE_OBJECT,~)

private:
void CheckMap(const ParameterMap& theMap)const {
assert(theMap.size()==ssize);
for(unsigned long
i(0);i<ssize;++i)assert(theMap.find(theVarNames)!=theMap.end());
}
std::string theVarNames[ssize];
};

#define PARAM_NAMES(n) n ,
static_cast<unsigned_long<sizeof(n)/sizeof(char*)> *>(0)


#undef MAX_PHACTORIES
#undef PARAM
#undef INNER_RETRIVE_OBJECT


#endif
-------------------------------------------------------------------------------------------------------------------------------
 

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

Similar Threads


Members online

Forum statistics

Threads
473,744
Messages
2,569,480
Members
44,900
Latest member
Nell636132

Latest Threads

Top