Boost logo

Boost :

From: Steven Watanabe (steven_at_[hidden])
Date: 2006-08-01 15:37:39


AMDG

Alexander,

>
>Steven, do you have a document that describes your approach in more details? Does it support multimethods for binary operations?
>
>--
>Alexander Nasonov
>
>

No I don't have such a document so here are the main benefits and
drawbacks of my approach.
I didn't know about your library until yesterday so I only understand it
in general but I will try
to primarily note the differences:

I am using a table of function pointers and an unchecked variant
type(like Boost.Function)
instead of using virtual functions. Thus I avoid a heap
allocation for objects that are small enough and are correctly aligned.
On the other hand this implementation requires the cast methods to only
convert
to the exact type put in - not to any of it's base classes.

The class template erasure is declared as
template<class Operations, class Policy = default_policy, class Derived
= my_own_type>
class erasure;

Operations is a composite tree of operations to allow groups of
operations to
behave the same as single operations. So all three of these are equivalent

    typedef combine_operations<boost::mpl::vector<isreamable,
ostreamable> > ops;
    erasure<ops> any;

    typedef combine_operations<istreamable> ops;
    erasure<boost::mpl::vector<ops, ostreamable> > any;

    erasure<boost::mpl::vector<isreamable, ostreamable> > any;

Policy specifies the details of the object structure. I have implemented
a policy
that simulates a virtual function based approach but this seems to put
excessive strain on the compiler because of the extra dependencies it
introduces.

Derived is to allow members like assignment which return(*this) to
cast to the correct type.

Because of the two other parameters I don't allow things like
    erasure<istreamable, ostreamable>
like of Boost.Variant although I think I could add support for it(with
some ugly metafunctions and more coupling).

I have provided support for Boost.Ref
    int i;
    erasure<...> any(boost::ref(i));
    int& iref = erasure_cast<int&>(any);
    assert(&i == &iref);
    const int* iptr = erasure_cast<const int*>(*any);
    assert(iptr == & iref);

The price of allowing the addition of const(or volatile) is a very
repetitive section of code
and 1-2 extra integer comparisons.

One final optimization I have implemented is for conversions from
one type to a subset. I think that this will slow down the
assignment(especially in
a multi-threaded program) but will speed up subsequent operations on the
result.
    erasure<istreamable, ostreamable> any_io(1);
    erasure<ostreamable> any_out(any_io);
    assert(erasure_cast<const int&>(any_out) == 1);

I do not support multi-methods. Unfortunately, there is no way to
implement a binary
operation that takes two arbitrary types. The closest I can get is to
have an operation
that takes one arbitrary type and one type from some fixed set which may
depend on
the first type. I didn't even think of this possibility until you
mentioned it so I havn't actually
tried to implement it.

Finally, here is an example of how I am defining operations:

struct addition1 : operation<addition1> {
    template<class T, class Any>
    struct static_impl : implementation<void (T&, const Any&)> {
       static void execute(T& arg, const Any& other) {
          arg += erasure_cast<const T&>(other); //throws bad_erasure_cast
       }
    };
    template<class Base>
    struct partial_interface : Base {
       typename Base::erasure_full_t& operator+=(const typename
Base::erasure_full_t& other) {
          erasures::call<addition1>(*this, other); //throws
object_is_empty
          return(*erasures::full(this));
       }
    };
};

 From the above you can see that the syntax for defining operations is
somewhat less than ideal. The main problem I know of is that defining
overloaded functions requires a workaround to prevent the derived class
version from hiding the base class one. Thus the preceeding example becomes:

struct addition1 : operation<addition1> {
    template<class T, class Any>
    struct static_impl : implementation<void (T&, const Any&)> {
       static void execute(T& arg, const Any& other) {
          arg += erasure_cast<const T&>(other);
       }
    };
    template<class Base>
    struct partial_interface_impl : Base {
       typename Base::erasure_full_t& operator+=(const typename
Base::erasure_full_t& other) {
          erasures::call<addition1>(*this, other);
          return(*erasures::full(this));
       }
    };
    template<class Base>
    struct partial_interface : partial_interface_impl<Base> {
       using partial_interface_impl<Base>::operator+=;
    };
};

If you have any furthur questions I would be glad to answer them.

In Christ,
Steven Watanabe


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