
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