Re: [boost] Simple object factory: Checking signatures at runtime

AMDG "Marco Costalba" <mcostalba@gmail.com> wrote:
Sorry, but I don't understand, could you please post an example of how to use the dispatcher so to see 'struct implicit_conversion' at work ?
Thanks Marco
The code I posted was not well thought out. The set of implicit conversions should probably be global. I would want to write make_implicit_conversion<double, int>(); make_implicit_conversion<int, double>(); Somewhere during initialization to say that int is convertible to double and double to int. Here's a more complete implementation (warning untested) I'm not showing how to register functions. That would involve creating a function object that casts from void* to the actual type. A real implementation would probably also use Boost.Function instead of function pointers. struct wrap_typeinfo { friend bool operator<(const wrap_typeinfo& lhs, const wrap_typeinfo& rhs) { return(lhs.key.before(rhs.key)); } const std::type_info* impl; }; struct implicit_conversion { friend bool operator<(const implicit_conversion& lhs, const implicit_conversion& rhs) { return(lhs.from.before(rhs.from)); } const std::type_info* to; void* (*convert)(void*); void (*destroy)(void*); }; static std::multi_map<wrap_typeinfo, implicit_conversion> all_conversions; void* ignore_convert(void* x) { return(x); } void ignore_destroy(void*) {} static implicit_conversion no_conversion = { 0, 0, &ignore_convert &ignore_destroy }; template<class To> void destroy(void* victim) { delete static_cast<To*>(victim); } template<class From, class To> void* convert(void* from) { return new To(*static_cast<From*>(from)); } template<class From, class To> void make_implicit_conversion() { wrap_typeinfo from = { &typeid(From) }; implicit_conversion result = { &typeid(To), &convert<From, To>, &destroy<From, To> }; all_conversions.insert(std::make_pair(from, result)); } template<class R> struct dispatcher0 { public: R operator()() const { return(f0()); } private: R (*f0)(); }; template<class F> F get_function(F f, const std::type_info**, const implicit_conversion**) { return(f); } template<class F, class T> F get_function(const std::map<wrap_typeinfo, T>& m, wrap_typeinfo* types, const implicit_conversion** out) { std::map<wrap_typeinfo, T>::iterator iter = m.find(*types); if(iter != m.end()) { ++types; *out++ = &no_conversion; return(get_function<F>(t.value, types, out)); } else { BOOST_FOR_EACH(const implicit_conversion& conversion, all_conversions.equal_range(*types)) { std::map<wrap_typeinfo, T>::iterator iter = m.find(*conversion.from); if(iter != m.end()) { ++types; *out++ = &conversion; return(get_function<F>(iter->second, types, out)); } } } return(0); } template<int N> struct cleanup_array : cleanup_array<N - 1> { cleanup_array(implicit_conversion* c, void** data) : cleanup_array<N - 1>(c) { x = c[N - 1].convert(data[N-1]); } ~cleanup_array() { cleanup_array<N - 1>::conversions[N-1].destroy(x); } void* x; }; template<> struct cleanup_array<0> { implicit_conversion* conversions; cleanup_array(implicit_conversion* c, void**) : conversions(c) {} }; template<int N, int N2> void* get(cleanup_array<N2>& from) { return(static_cast<cleanup_array<N+1>&>(from).x); } template<class R> struct dispatcher1 { public: template<class T0> R operator()(T0 t0) const { wrap_typeinfo types[1] = { &typeid(T0) }; implicit_conversion* conversions[1]; R (*f)(void*) = get_function<R(*)(void*)>(f1, types, conversions); void* elements[1] = { &t0 }; cleanup_array<1> data(conversions, elements); return(f(get<0>(data))); } private: std::map<wrap_typeinfo, R(*)(void*)> f1; }; template<class R> struct dispatcher2 { public: template<class T0, class T1> R operator()(T0 t0, T1 t1) const { wrap_typeinfo types[2] = { &typeid(T0), &typeid(T2) }; implicit_conversion* conversions[2]; R (*f)(void*) = get_function<R(*)(void*, void*)>(f2, types, conversions); void* elements[2] = { &t0, &t1 }; cleanup_array<2> data(conversions, elements); return(f(get<0>(data), get<1>(data))); } private: std::map<wrap_typeinfo, std::map<wrap_typeinfo, R(*)(void*, void*)>
f2; };
In Christ, Steven Watanabe

On 9/8/07, Steven Watanabe <watanabesj@gmail.com> wrote:
The code I posted was not well thought out. The set of implicit conversions should probably be global. I would want to write
make_implicit_conversion<double, int>(); make_implicit_conversion<int, double>();
Somewhere during initialization to say that int is convertible to double and double to int.
Here's a more complete implementation (warning untested) I'm not showing how to register functions. That would involve creating a function object that casts from void* to the actual type. A real implementation would probably also use Boost.Function instead of function pointers.
----- cut---
struct implicit_conversion { friend bool operator<(const implicit_conversion& lhs, const implicit_conversion& rhs) { return(lhs.from.before(rhs.from)); } const std::type_info* to; void* (*convert)(void*); void (*destroy)(void*); };
If you don't mind will try to 'annotate' your code, just to be sure I have understood correctly. The below struct is used to store pointers to a type agnostic 'convert' function as defined below.
template<class From, class To> void* convert(void* from) { return new To(*static_cast<From*>(from)); }
The trick is that only the interface (argument + return type) is type agnostic, but inside a call to an actual c'tor is done. The c'tor to call is hardwired during instantation:
template<class From, class To> void make_implicit_conversion() { wrap_typeinfo from = { &typeid(From) };
'typeid' is needed because it will be compared with the type passed at runtime.
implicit_conversion result = { &typeid(To), &convert<From, To>, &destroy<From, To> };
'result' is type agnostic also if has been bounded to 'convert' and 'destroy', so that it can be stored in a map.
all_conversions.insert(std::make_pair(from, result)); }
This is the first phase the type hiding... These two 'get_function' functions traverse recursively all the type list and create a corresponding list of pointers to 'convert' functions. The first 'little' get_function() is used as stop condition duing type list walking
template<class F> F get_function(F f, const std::type_info**, const implicit_conversion**) { return(f); }
The big one is divided in two parts, the first is executed if type does not need conversion because is already matched. The second instead iterates trough the conversion list to find a matching 'from' type and, in this case, append the proper pointer to the 'convert' function.
template<class F, class T> F get_function(const std::map<wrap_typeinfo, T>& m, wrap_typeinfo* types, const implicit_conversion** out) { std::map<wrap_typeinfo, T>::iterator iter = m.find(*types); if(iter != m.end()) { ++types; *out++ = &no_conversion; return(get_function<F>(t.value, types, out)); } else { BOOST_FOR_EACH(const implicit_conversion& conversion, all_conversions.equal_range(*types)) { std::map<wrap_typeinfo, T>::iterator iter = m.find(*conversion.from); if(iter != m.end()) { ++types; *out++ = &conversion; return(get_function<F>(iter->second, types, out)); } } } return(0); }
Finally, once the list of pointers to 'convert' functions is ready the below 'cleanup_array' also recursively implicitly convert the values to the right one calling the 'convert' functions that have inside the call to the implicit conversion constructors for the given type.
template<int N> struct cleanup_array : cleanup_array<N - 1> { cleanup_array(implicit_conversion* c, void** data) : cleanup_array<N - 1>(c) { x = c[N - 1].convert(data[N-1]); } ~cleanup_array() { cleanup_array<N - 1>::conversions[N-1].destroy(x); } void* x; };
template<> struct cleanup_array<0> { implicit_conversion* conversions; cleanup_array(implicit_conversion* c, void**) : conversions(c) {} };
template<int N, int N2> void* get(cleanup_array<N2>& from) { return(static_cast<cleanup_array<N+1>&>(from).x); }
---- cut -------------
In Christ, Steven Watanabe
Thanks Steven, very helpful and instructive! I'm now converting 'simple_factory.hpp' to use boost::fusion, well indeed conversion is terminated, but because is the first time I use boost::fusion probably I'm missing a lot of opportunities to simplify the thing, until now I have just removed about 1/3 of code lines from previous, home grown version, not a lot to compensate a X3 increase in compilation times ;-) As soon as I'm feeling a bit more confident with boost::fusion I will try to integrate your idea. Thanks again Marco

On 9/9/07, Marco Costalba <mcostalba@gmail.com> wrote:
I'm now converting 'simple_factory.hpp' to use boost::fusion, well indeed conversion is terminated, but because is the first time I use boost::fusion probably I'm missing a lot of opportunities to simplify the thing, until now I have just removed about 1/3 of code lines from previous, home grown version, not a lot to compensate a X3 increase in compilation times ;-)
In case someone is interested... http://digilander.libero.it/mcostalba/factory_boost/prog_15_main_fusion.cpp.... http://digilander.libero.it/mcostalba/factory_boost/prog_16_simple_factory_f... Marco

On Sun, 2007-09-09 at 17:15 +0200, Marco Costalba wrote:
On 9/9/07, Marco Costalba <mcostalba@gmail.com> wrote:
I'm now converting 'simple_factory.hpp' to use boost::fusion, well indeed conversion is terminated, but because is the first time I use boost::fusion probably I'm missing a lot of opportunities to simplify the thing, until now I have just removed about 1/3 of code lines from previous, home grown version, not a lot to compensate a X3 increase in compilation times ;-)
In case someone is interested...
http://digilander.libero.it/mcostalba/factory_boost/prog_15_main_fusion.cpp.... http://digilander.libero.it/mcostalba/factory_boost/prog_16_simple_factory_f...
Why did you choose to use a multimap for the container in the factory? Can you store any type in the factory? That is your example shows you storing ParentClass and the derived types plus OtherClass. Are both a base class? Or is it two separate instances of the Factory therefore two singleton factories available to produce objects? Stephen

On 9/19/07, Stephen Torri <storri@torri.org> wrote:
Why did you choose to use a multimap for the container in the factory?
To allow ctor argument overloading. Factory<BaseClass>::register_class<DerivedClass*(int)>("Derived"); // ctor nr 1 Factory<BaseClass>::register_class<DerivedClass*(const char*)>("Derived"); // ctor nr 2 Base* obj1 = Factory<BaseClass>::object("Derived", 7); // calls ctor nr 1 Base* obj2 = Factory<BaseClass>::object("Derived", "this calls ctor Nr 2"); So you have many instances of the key ( "Derived" in this case) in your map, each one associated to different argument types or also argument's number because you can add as example: Factory<BaseClass>::register_class<DerivedClass*(int, float)>("Derived"); // ctor nr 3 Base* obj3 = Factory<BaseClass>::object("Derived", 2, 3.14); // calls ctor nr 3
Can you store any type in the factory? That is your example shows you storing ParentClass and the derived types plus OtherClass. Are both a base class? Or is it two separate instances of the Factory therefore two singleton factories available to produce objects?
Exactly, you have two singletons (of different types): Factory<BaseClass> and Factory<OtherClass> In general terms you have one singleton factory type for each object hierarchy. In case you foreseen to create objects all belonging to the same base type you can wrap the functions to avoid user to specify to what singleton you refer: bool myRegister(...) { return Factory<MyBaseClass>::register(...); } MyBaseClass* myObject(...) { return Factory<MyBaseClass>::object(...); } etc... Marco
participants (3)
-
Marco Costalba
-
Stephen Torri
-
Steven Watanabe