
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