Daniel T. wrote:
Quote:
Despite the fact that changing main() was exactly what the OP wanted to
avoid. I.E., your solution didn't solve the OPs problem.
You seem to be reading a lot into it AFAICS. It seems to me the OP just
wanted to know how to use a map. Anyway I decided to work on my
previous efforts to incorporate some of the ideas.
I have however tried to avoid intruding into the derived classes so
they can be used with other factories or alone.
Factory also tries to be more generic.
Poorest part is probbably the stream input which doesnt get any
checking, could also be made more generic.
Probably over the top for what the O.P wanted, but it beats watching
the box:-)
regards
Andy Little
Tested on VC7.1 and gcc4.1. should compile OK as one big file:
/*
factory demo
*/
// comment out to compile as separate files
// with headers
#define ALLINONE
#ifndef BASIC_MODEL_HPP_INCLUDED
#define BASIC_MODEL_HPP_INCLUDED
#include <boost/shared_ptr.hpp>
// abc model class
struct BasicModel{
typedef BasicModel base_type;
typedef boost::shared_ptr<BasicModelptr;
virtual ~BasicModel(){}
virtual const char * sig() const = 0;
};
#endif
//----------------------------------
#ifndef DEFAULT_MODEL_HPP_INCLUDED
#define DEFAULT_MODEL_HPP_INCLUDED
#include "basic_model.hpp"
// some concrete model
struct default_Model : BasicModel{
default_Model(int a, int b , int c){}
const char * sig() const {return "default model";}
};
#endif
//---------------------------------
#ifndef MODELA_HPP_INCLUDED
#define MODELA_HPP_INCLUDED
#ifndef ALLINONE
#include "basic_model.hpp"
#endif
// other concrete model
struct Modela : BasicModel{
Modela(int a){}
const char * sig() const {return "model a";}
};
#endif
//---------------------------------
#ifndef MODELABC_HPP_INCLUDED
#define MODELABC_HPP_INCLUDED
#include <iostream>
#ifndef ALLINONE
#include "basic_model.hpp"
#endif
#include <string>
// another concrete model
struct Modelabc : BasicModel{
Modelabc(int a, int b,std::string const & c){}
const char * sig() const {return "model abc";}
static ptr make( std::istream & in)
{
int a, b;
std::string c;
in >a >b >c;
return ptr (new Modelabc(a,b,c));
}
static const char* id(){ return "abc";}
};
#endif
//---------------------------------
#ifndef FACTORY_HPP_INCLUDED
#define FACTORY_HPP_INCLUDED
// generic factory traits classes
// per factory id
template <typename Factory, typename Object>
struct construction_id;
// per factory creation function
template <typename Factory, typename Object>
struct construction_function;
// get base class utility
template <typename Object>
struct get_base_type{
typedef typename Object::base_type type;
};
#endif
//---------------------------------
#ifndef ISTREAM_FACTORY_HPP_INCLUDED
#define ISTREAM_FACTORY_HPP_INCLUDED
#ifndef ALLINONE
#include "factory.hpp"
#endif
#include <map>
#include <string>
#include <iostream>
#include <boost/function.hpp>
#include <boost/function_equal.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <stdexcept>
// istream factory. create an object via an istream
template <typename BaseObject>
class istream_factory{
public:
// probably shouldnt be public ...
typedef boost::function<typename BaseObject::ptr(std::istream &)>
function;
typedef std::map< std::string, functionobject_map_type;
object_map_type object_maker;
// register a derived type
template <typename Object>
void add( const char* id)
{
BOOST_STATIC_ASSERT((boost::is_same<BaseObject, typename
get_base_type<Object>::type>::value));
object_maker[id] =
construction_function<istream_factory,Object>();
}
typename BaseObject::ptr make(std::istream & in)
{
std::string id;
in >id;
typename object_map_type::const_iterator i =
object_maker.find(id);
if( i == object_maker.end()){
throw std::invalid_argument("invalid construction id in
istream_factory");
}
return i->second(in);
}
};
// Use Daniel T's trick for factory init
template <typename BaseObject>
istream_factory<BaseObject>& get_istream_factory()
{
static istream_factory<BaseObjectfactory;
return factory;
}
// User id get function
template<typename Object>
const char * get_istream_construction_id()
{
typedef typename get_base_type<Object>::type base;
construction_id<istream_factory<base>,Objectid;
return id();
}
// struct to register type
// instantiate a static version in cpp file per object
template <typename Object>
struct register_with_istream_factory{
void register_()
{
static bool is_registered = false;
if( ! is_registered){
typedef typename get_base_type<Object>::type base;
construction_id<istream_factory<base>,Objectid;
get_istream_factory<base>(). template add<Object>(id());
is_registered = true;
}
}
register_with_istream_factory()
{
register_();
}
};
#endif
//---------------------------------
#ifndef BASIC_MODEL_IF_HPP_INCLUDED
#define BASIC_MODEL_IF_HPP_INCLUDED
//default non intrusive support for istream_factory
// for BasicModel derived classes
#ifndef ALLINONE
#include "basic_model.hpp"
#include "istream_factory.hpp"
#endif
// For O.P most( all) classes have common ctor params
// this partial specialisation covers those cases
// further specialisation is possible
// as shown below
template <typename Default>
struct construction_function<istream_factory<BasicModel>, Default>
{
BasicModel::ptr operator()( std::istream & in)const
{
int a, b, c;
in >a>b >c;
return BasicModel::ptr (new Default(a,b,c));
}
};
#endif
//----------------------------------
#ifndef DEFAULT_MODEL_IF_HPP_INCLUDED
#define DEFAULT_MODEL_IF_HPP_INCLUDED
// each derived class needs own id
//
#ifndef ALLINONE
#include "default_model.hpp"
#include "istream_factory.hpp"
#endif
template <>
struct construction_id<istream_factory<BasicModel>,defaul t_Model>
{
const char* operator()()const
{
return "default";
}
};
#endif
//----------------------------------
#ifndef MODELA_IF_HPP_INCLUDED
#define MODELA_IF_HPP_INCLUDED
#ifndef ALLINONE
#include "modela.hpp"
#include "istream_factory.hpp"
#endif
// customised istream_factory construction
// for Modela
template <>
struct construction_id<istream_factory<BasicModel>,Modela >
{
const char* operator()()const
{
return "a";
}
};
// Modela different ctor params so its
// construction function is further specialised...
template <>
struct construction_function<istream_factory<BasicModel>, Modela>
{
BasicModel::ptr operator()( std::istream & in)const
{
int a;
in >a;
return BasicModel::ptr (new Modela(a));
}
};
#endif
//------------------------------------
#ifndef MODELABC_IF_HPP_INCLUDED
#define MODELABC_IF_HPP_INCLUDED
// customised istream_factory construction
// for Modelabc
#ifndef ALLINONE
#include "modelabc.hpp"
#include "istream_factory.hpp"
#endif
template <>
struct construction_id<istream_factory<BasicModel>,Modela bc>
{
const char* operator()()const
{
return Modelabc::id();
}
};
// etcetera...
template <>
struct construction_function<istream_factory<BasicModel>, Modelabc>
{
BasicModel::ptr operator()( std::istream & in)const
{
return Modelabc::make(in);
}
};
#endif
//----------------SOURCE-----------------
//-----------main.cpp-----------
#ifndef ALLINONE
#include "default_model_if.hpp"
#include "modela_if.hpp"
#include "modelabc_if.hpp"
#endif
#include <sstream>
int main()
{
try{
istream_factory<BasicModel& factory
= get_istream_factory<BasicModel>();
std::stringstream stream;
int a =1, b= 2, c =3;
stream << get_istream_construction_id<default_Model>()
<< ' ' << 1 << ' '<< 2 << ' '<< 3 << '\n';
stream << get_istream_construction_id<Modela>()
<< ' ' << 4 << '\n';
stream << get_istream_construction_id<Modelabc>()
<< ' ' << 5 << ' ' << 6 << ' ' << "hello" << '\n';
std::cout << "stream contains\n";
std::cout << stream.str();
std::cout << "---------\n\n";
BasicModel::ptr p = factory.make(stream);
std::cout << p->sig() <<'\n';
p = factory.make(stream);
std::cout << p->sig() <<'\n';
p = factory.make(stream);
std::cout << p->sig() <<'\n';
}
catch (std::exception & e){
std::cout << e.what() <<'\n';
}
}
// register various models...
//-------default_model_if.cpp---
#ifndef ALLINONE
#include "basic_model_if.hpp"
#include "default_model_if.hpp"
#endif
static register_with_istream_factory<default_Model>
default_Model_istream_registered
= register_with_istream_factory<default_Model>();
//-------modela_if.cpp-------
#ifndef ALLINONE
#include "modela_if.hpp"
#endif
// Modela istream_factory regsitry cpp file
static register_with_istream_factory<Modela>
Modela_istream_registered
= register_with_istream_factory<Modela>();
//-----modelabc_if.cpp-------
#ifndef ALLINONE
#include "modelabc_if.hpp"
#endif
static register_with_istream_factory<Modelabc>
Modelabc_istream_registered
= register_with_istream_factory<Modelabc>();
// --------end-----------