Boost logo

Boost :

From: Marco Costalba (mcostalba_at_[hidden])
Date: 2007-09-09 10:47:16


On 9/8/07, Steven Watanabe <watanabesj_at_[hidden]> 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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk