Preamble
This paper provides a way to solve famous "multimethods problem". The main merits of the proposed solution are:
- no use of type casts of any kind (dynamic, static, reinterpret, const or C-style)
- no use of RTTI;
- no use of preprocessor;
- strong type safety;
- separate compilation;
- constant time of multimethod execution;
- no dynamic memory allocation (via
new or malloc) during multimethod call;
- no use of nonstandard libraries;
- only standard C++ features is used.
The Problem
Let's consider following example.
Suppose we have a simple hierarchy of geometrical shapes and want to write a function answering the question: "do two (or more) shapes intersect". Shapes hierarchy may look as follows.

Function, that checks for shapes intersections, must work with IShape references, i.e. in polymorphic manner. For example:
void foo( IShape_& _shape0, IShape_& _shape1 ) {
bool f = CheckIntersection( _shape0, _shape1 );
}
...
Rect_ rect( Point_( 1, 1), Point_( 2, 2) );
Triangle_ tri( Point_( 0, 0), Point_( 2, 0), Point_( 0, 2) );
foo( rect, tri );
It corresponds to object-oriented view of virtual methods execution, where we don't care about concrete types of objects, because appropriate method is called automatically, via virtual method invocation mechanism [1].
Indeed, it's easy to write specialized intersection functions (for intersecting rectangle against triangle, for example). However, when CheckIntersection is called, there is no information about concrete types of its arguments. And there is no compiler support to do this job.
Run-time type determination
Nevertheless, there is a regular way to determine concrete object type at run-time using well-known Visitor pattern [2]. This method is also called dual dispatching. Each visitable object should implement method Visit that accepts reference to special object - dispatcher. Dispatcher should have a set of Dispatch methods that accepts references to concrete types of visitable objects. Simple example of Visitor pattern usage is shown below.
struct Triangle_;
struct Rect_;
struct IDispatcher_ {
virtual void Dispatch( Triangle_& _shape ) = 0;
virtual void Dispatch( Rect_& _shape ) = 0;
};
struct IShape_ {
virtual void Visit( IDispatcher_& _dsp ) = 0;
};
struct Triangle_ : public IShape_ {
virtual void Visit( IDispatcher_& _dsp ) {
_dsp.Dispatch( *this );
}
};
struct Rect_ : public IShape_ {
virtual void Visit( IDispatcher_& _dsp ) {
_dsp.Dispatch( *this );
}
};
struct Dispatcher_ : public IDispatcher_ {
virtual void Dispatch( Triangle_& _shape ) {
cout << "This is a triangle." << endl;
}
virtual void Dispatch( Rect_& _shape ) {
cout << "This is a rect." << endl;
}
};
void PrintType( IShape_& _shape ) {
Dispatcher_ dsp;
_shape.Visit( dsp );
}
int main()
{
Rect_ rect;
Triangle_ triangle;
PrintType( rect );
PrintType( triangle );
return 0;
}
This programs prints:
This is a rect.
This is a triangle.
This method has a constant overhead: two virtual method calls plus object construction on stack.
Visitable shapes
We will use the Visitor pattern to implement multimethods. All particular shapes should be visitable objects, i. e. all of them should implement Visit method. We can write the following code:
class IShapesDsp_;
struct IShape_ {
typedef IShapesDsp_ IDispatcher;
virtual void Visit( IDispatcher& _dispatcher ) = 0;
};
#include "IShape.h"
#include "Point.h"
class Rect_
: public IShape_
{
public:
Rect_( Point_ const& _top_left, Point_ const& _bottom_right );
Point_ const& GetTopLeft();
Point_ const& GetBottomRight();
virtual void Visit( IDispatcher& _dispatcher );
private:
Point_ top_left;
Point_ bottom_right;
};
Note that when we declare Visit methods we don't need to have a full definition of IShapesDsp, forward declaration is ok. In other words, declaration of shapes depends only on IShapesDsp name.
Dispatcher interface generation
We will generate dispatcher interface from typelist of used shapes. Typelists are discussed in [3]. Very lightweight type-list implementation will be used for simplicity.
Using partial template specialization mechanism [4], dispatcher interface will be generated using following code:
template< typename TItem >
struct IDispatchPart_ {
virtual void Dispatch( TItem& _item ) = 0;
};
template< typename TItems >
struct IDispatcher_
: public IDispatchPart_< typename TItems::item >
, public IDispatcher_< typename TItems::next > {};
template<>
struct IDispatcher_< ListEnd_ > {};
class Rect_;
class Square_;
class Triangle_;
class RightTriangle_;
typedef TypeList_< Rect_,
TypeList_< Square_,
TypeList_< Triangle_,
TypeList_< RightTriangle_
> > > > Shapes_;
class IShapesDsp_ : public IDispatcher_< Shapes_ > {};
The following hierarchy will be generated:

Note that the declaration of IShapesDsp doesn't depend on shapes declarations, it depends only on their names.
Shapes implementation
Although shapes don't depend on IShapesDsp declaration, their implementations do. Now, when IShapesDsp is declared, we can implement shapes' Visit methods.
#include "IShapesDsp.h"
template< typename TShape, typename IDispatcher >
void VisitImpl( TShape& _self, IDispatcher& _dispatcher )
{
IDispatchPart_< TShape >& part = _dispatcher;
part.Dispatch( _self );
}
#include "Rect.h"
#include "VisitImpl.h"
void Rect_::Visit( IDispatcher& _dispatcher ) {
VisitImpl( *this, _dispatcher );
}
void Triangle_::Visit( IDispatcher& _dispatcher ) {
VisitImpl( *this, _dispatcher );
}
Dispatcher generation
Now we have the dispatcher interface and the set of nodes, which can be visited by this interface implementations. These implementations will be generated from type-list using the following code:
template < typename TItems,
typename IDsp,
typename TMixedImpl
>
class Dispatcher_
: public Dispatcher_< typename TItems::next, IDsp, TMixedImpl >
{
typedef Dispatcher_< typename TItems::next, IDsp, TMixedImpl > TBase;
typedef typename TItems::item TItem;
virtual void Dispatch( TItem& _item ) {
Implement( _item );
}
};
template < typename IDsp,
typename TMixedImpl
>
class Dispatcher_<ListEnd_, IDsp, TMixedImpl >
: public IDsp
, public TMixedImpl {};
This Dispatcher overrides all pure virtual Dispatch methods that were declared in IDsp.Implementations of these methods are redirected to a mixin class, passed as TMixedImpl parameter.
MultiMethods gateway
Now, when we know how to generate dispatchers, we can write our CheckIntersection function. It is a non-template free function that can be located in a *.cpp file. We will overload CheckIntersection for each arity of multimethod. n-ary multimethod is executed in n steps. Each i-th step begins with dispatcher creation. This dispatcher is used to determine concrete type of i-th multimethod's argument. Information of this type is propagated to the next step and so on. At the last step, where types of all arguments are known, fully specialized implementation is invoked.
Here are the dual and triple versions:
struct IntersectionsBulk_
{
template< typename TShape0, typename TShape1 >
struct DualImpl_ : public DualIntersecter_< TShape0, TShape1 > {};
template< typename TShape0, typename TShape1, typename TShape2 >
struct TripleImpl_
: public TripleIntersecter_< TShape0, TShape1, TShape2 > {};
};
bool CheckIntersection( IShape_& _shape0, IShape_& _shape1 )
{
Dispatcher_< Shapes_, IShapesDsp_, FirstStep_<2, IntersectionsBulk_> > dsp;
dsp.UnknownArg( 1, _shape1 );
_shape0.Visit( dsp );
return dsp.GetResult();
}
bool CheckIntersection( IShape_& _shape0, IShape_& _shape1, IShape_& _shape2 )
{
Dispatcher_< Shapes_, IShapesDsp_, FirstStep_<3, IntersectionsBulk_> > dsp;
dsp.UnknownArg( 1, _shape1 );
dsp.UnknownArg( 2, _shape2 );
_shape0.Visit( dsp );
return dsp.GetResult();
}
Let's look at FirstStep template class declaration:
template< int Arity, typename TImplBulk >
struct FirstStep_ : public FirstStepBase_< Arity > {
protected:
template< typename TShape0 >
inline void Implement( TShape0& _arg0 ) {
Dispatcher_< Shapes_, IShapesDsp_, SecondStep_< TShape0,
Arity, TImplBulk > > dsp;
dsp.Arg0( _arg0 );
for ( int i = 1; i < Arity; ++i )
dsp.UnknownArg( i, *unknown[i] );
unknown[1]->Visit( dsp );
SetResult( dsp.GetResult() );
}
};
It has two template parameters:
- arity of executed multimethod;
- intersection checker specializations container.
Also it has one template method - Implement, which will be called from an outer dispatcher. In Implement method the next dispatcher is created. Note, that SecondStep receives type information about first argument and a typed reference to it.
SecondStep template class has two specializations. There is a general version that works like FirstStep and a specialized binary version, which is shown below.
template< typename TShape0, typename TImplBulk >
struct SecondStep_< TShape0, 2, TImplBulk > : public SecondStepBase_< TShape0, 2 > {
protected:
template< typename TShape1 >
inline void Implement( TShape1& _arg1 ) {
typedef typename TImplBulk::DualImpl_< TShape0, TShape1 >::Impl_ TImpl;
SetResult( TImpl::Implement( *arg0, _arg1 ) );
}
};
Specialized multimethod implementations
Specialized versions of intersection checkers are called via static methods of XXXIntersecter template class. There is a dedicated class for each arity. These classes should be defined in place where multimethod execution is done (Intersection.cpp in our example).
The DualIntersecter class is for dual multimethods. Firstly, forward declaration of DualIntersecter is provided:
template< typename TShape0, typename TShape1 >
struct DualIntersecter_;
After that the specialized versions can be added. For crossing rectangles against triangles we will write:
class Rect_;
class Triangle_;
template<>
struct DualIntersecter_< Rect_, Triangle_ >
{
typedef DualIntersecter_< Rect_, Triangle_ > Impl_;
static bool Implement( Rect_& _shape0, Triangle_& _shape1 );
};
bool DualIntersecter_< Rect_, Triangle_ >
::Implement( Rect_& _shape0, Triangle_& _shape1 )
{
std::cout << "Intersecting rectangle against triangle." << std::endl;
return true;
}
It is obvious that CheckIntersection function is commutative, but compiler doesn't know about it. We must provide a reverse specialization for crossing triangles against rectangles. It can be done very easy:
template <typename TShape0, typename TShape1>
struct DualReverse_
{
typedef DualReverse_< TShape0, TShape1 > Impl_;
static bool Implement( TShape0& _arg0, TShape1& _arg1 )
{
typedef DualIntersecter_< TShape1, TShape0 >::Impl_ TImpl;
return TImpl::Implement( _arg1, _arg0 );
}
};
template<>
struct DualIntersecter_< Triangle_, Rect_ >
: public DualReverse_< Triangle_, Rect_ > {};
Partial intersection specialization
Suppose we don't want to write specialized versions to intersect squares against any another shapes, because it can be done via rectangle facilities. Here we can use partial intersection specialization. Note that we should declare three specializations to avoid ambiguity.
#include "Square.h"
template< typename TShape1 >
struct DualIntersecter_< Square_, TShape1 >
: public DualIntersecter_< Rect_, TShape1 > {};
template<>
struct DualIntersecter_< Square_, Square_ >
: public DualIntersecter_< Rect_, Rect_ > {};
template< typename TShape0 >
struct DualIntersecter_< TShape0, Square_ >
: public DualReverse_< TShape0, Square_ > {};
Specialization redirection
Note that each specialization declares Impl typedef. In trivial cases specialization simply re-declares itself. But this typedef allows us to overcome some C++ limitations. Suppose we want to write specialization, that should intersect any two shapes, which are convertible to rectangles. But C++ template specialization lookup algorithm is unable to work with relations between types. That's why we will declare most general DualIntersecter specialization that checks relations between types, provides specialization for crossing rectangular shapes and in other cases redirects to DualIntersecter2. This specialization redirection pattern can be applied sequentially.
#include "TypeStuff.h"
#include "Rect.h"
#include "Square.h"
struct RectsIntersecter_ {
typedef RectsIntersecter_ Impl_;
static bool Implement( Rect_& _shape0, Rect_& _shape1 );
};
template< typename TShape0, typename TShape1 >
struct DualIntersecter2_;
template< typename TShape0, typename TShape1 >
struct DualIntersecter_ {
static bool const both_rectangular =
IsConvertible_< TShape0, Rect_ >::value &&
IsConvertible_< TShape1, Rect_ >::value;
typedef typename
If_< both_rectangular,
RectsIntersecter_::Impl_,
typename DualIntersecter2_< TShape0, TShape1 >::Impl_ >::type
Impl_;
};
bool RectsIntersecter_::Implement( Rect_& _shape0, Rect_& _shape1 ) {
std::cout << "Intersecting rectangle against rectangle." << std::endl;
return true;
}
Instead of specialization redirection nested if meta-functions can be used.
Hierarchy furling
Suppose we don't want to write specialized versions to intersect equilateral triangles against any other shapes, because it can be done via triangle ones. Solution is very easy - just don't override Visit method in EquilateralTriangle and don't include this type in shapes type-list.
Integrity
In this approach compiler maintains integrity. It is impossible to forget some specialization, because compiler refuses to accept such programs. For example, it's impossible to comment any of dual specializations #include directives in Intersection.cpp.
However, for debug and development reasons, some specializations subset can be temporarily replaced by a most general specialization of XXXIntersecter. We use this technique for triple intersections.
Overhead
Multimethod execution has constant overhead. This overhead depends only on multimethod arity. When n-ary multimethod is executed:
- 2 * n virtual methods are called;
- n auxiliary objects are constructed on stack;
- n * (n + c) non-virtual get/set methods are called (may be inlined), where c is a small constant.
Dependencies
Let's look on our example dependency diagram:

As you can see, client code depends only on shapes library interface. Client code doesn't include typelists, dispatcher interface and its implementations. That's why there is no compilation time growing. And even if specializations configuration changes, client code will not be recompiled.
Compilation in separate modules
Moreover, some shapes and their intersection checkers' specializations can be moved into separate module (DLL).The essence of the matter is that Visit method can be implemented using dynamic_cast. It can receive a reference to some polymorphic object and cast it to the appropriate IDispatchPart. But this solution isn't discussed here in detail, because it breaks the beautiful type safety concept.
History
This work based on MultiMethods Implementation via Deferred Dispatching. The main advantage against previous solution is a separate compilation possibility.
Acknowledgements
Special thanks to:
- Alexander Evdokimov;
- Michael Litvinov.
References
- Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley, third edition, August 1997.
- Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns - Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.
- Andrei Alexandrescu. Modern C++ Design. Addison-Wesley, C++ In-Depth series. 2001.
- David Vandevoorde and Nicolai M. Josuttis. C++ Templates: The Complete Guide. Addison-Wesley, 2003.
|
|
 |
 | class Dispatcher_, class IShapesDsp_, and Dispatch() geoyar | 13:36 10 Oct '07 |
|
 |
Hi,
You defined
class Dispatcher_ : public Dispatcher_< typename TItems::next, IDsp, TMixedImpl > { typedef Dispatcher_< typename TItems::next, IDsp, TMixedImpl > TBase; typedef typename TItems::item TItem;
virtual void Dispatch( TItem& _item ) { Implement( _item ); // this method should be provided by TMixedImpl } };
//Deferred dispatcher implementation stub. template < typename IDsp, typename TMixedImpl > class Dispatcher_ : public IDsp , public TMixedImpl {};
The compiler will expand this class to include virtual functions Dispatch for all items in TItems.
You use this class as
Dispatcher_< Shapes_, IShapesDsp_, FirstStep_<2, IntersectionsBulk_> > dsp;
Definitions and declarations are:
typedef TypeList_< Rect_, TypeList_< Square_, TypeList_< Triangle_, TypeList_< RightTriangle_ > > > > Shapes_;
class IShapesDsp_ : public IDispatcher_< Shapes_ > {};
template< typename TItems > struct IDispatcher_ : public IDispatchPart_< typename TItems::item > , public IDispatcher_< typename TItems::next > {};
template< typename TItem > struct IDispatchPart_ { virtual void Dispatch( TItem& _item ) = 0; };
IShapesDsp_ will also be expanded to include the set of Dispatch() for all shapes.
So I understand that the Dispatch functions are included into Dispatcher_ twice, via inheritance from Dispatcher_ and from IDsp_.
Am I wrong?
geoyar
|
|
|
|
 |
|
 |
Hi!
geoyar wrote: So I understand that the Dispatch functions are included into Dispatcher_ twice, via inheritance from Dispatcher_ and from IDsp_.
As you can see, Dispatch() methods are declared as pure virtual methods in the IDispatcher_ class. Then, these methods are overridden by corresponding Dispatch() methods from Dispatcher_ class. So, there is no of duplication. There is overriding.
-- Danil
|
|
|
|
 |
 | VisitImpl __Skitleer__ | 23:27 26 Jun '07 |
|
 |
Hello. I've a little question that bugs me. In VisitImpl.h : //VisitImpl.h #include "IShapesDsp.h"
template< typename TShape, typename IDispatcher > void VisitImpl( TShape& _self, IDispatcher& _dispatcher ) { IDispatchPart_< TShape >& part = _dispatcher; part.Dispatch( _self ); }
Why is the bold part necessary ? couldn't we just write
_dispatcher.Dispatch( _self );
Great article anyway.
Stef
|
|
|
|
 |
|
 |
Hello!
No, we couldn't.
As you can see in the provided example, there are four appropriate Dispatch() methods in the IDispatcher class. They are declared in IDispatchPart_<Rect_>, IDispatchPart_<Square_>, IDispatchPart_<Triangle_> and IDispatchPart_<RightTriangle_>. It's because of the fact that the final dispatcher is inherited from these partial dispatchers recursively.
All of these Dispatch() methods has the same signature. That's why the direct call will be ambiguous.
So we get the particular IDispatchPart_< TShape > partial dispatcher and call its Dispatch() method. It's OK to get the partial dispatcher without type cast because it is a parent of the final dispatcher.
That's all!
Thanks for you interest!
-- Danil
P.S. Btw, I've encountered that there is no link to a library on SourceForge. Here it is: http://sourceforge.net/projects/mmdd
The code is a bit more complicated but at this level of complexity it's no problem, I think As an advantage the code in library is more clear and general.
|
|
|
|
 |
|
 |
Hi, thx for the quick reply.
Still a bit unclear for me though as I thought that the Dispatch method had different signature though ( Dispatch(Rect_&), Dispatch(Square_&), Dispatch(Triangle_&), Dispatch(RightTriangle_&) ) actually. Or does the fact the all these class are part of the same class Hierarchy prevent the compiler to resolve the overloading ?
Also, if we didn't need to get the IDispatchPart in the VisitImpl method, would it be possible to generate the Dispatcher interface this way :
//IDispatch.h
template< typename TItems > struct IDispatcher_ : public IDispatchPart_< typename TItems::item > , public IDispatcher_< typename TItems::next > { virtual void Dispatch( TItems::item& _item ) = 0; };
//Deferred dispatcher interface stub. template<> struct IDispatcher_< ListEnd_ > {};
Cheers,
Stef
|
|
|
|
 |
|
 |
Hi!
Sorry for a delay 
I'm a little bit out of sync, so my prior answer isn't absolutely clear. Of course, all Dispatch() methods has different signatures (they differs only by the type of the argument). But the overloading resolution doesn't work with the inheritance. For example, it's described here.
But we can use "using declaration". So you are completely right and we can dismiss IDispatchPart_ interface at all.
The code looks like this:
template< typename TItems > struct IDispatcher_ : public IDispatcher_< typename TItems::next > { typedef typename TItems::item TItem; virtual void Dispatch( TItem& _item ) = 0; using typename IDispatcher_< typename TItems::next >::Dispatch; };
template<> struct IDispatcher_< ListEnd_ > { void Dispatch(ListEnd_& ) {} };
-- Danil
|
|
|
|
 |
 | Very convoluted solution kackermann | 17:14 30 Jul '06 |
|
 |
Just use basic polymorphism.
Define a base class with a DoesIntersect interface
For every derived shape class impliment...
bool ThisShape::DoesIntersect(DifferentShape1* pShape) bool ThisShape::DoesIntersect(DifferentShape2* pShape) bool ThisShape::DoesIntersect(DifferentShape3* pShape) etc.
Now you can call..
result = BaseShape->DoesIntersect(DifferentShape)
Nothing could be simpler. I use it all the time.
|
|
|
|
 |
 | Clever Shawn Poulson | 3:15 30 Jun '06 |
|
 |
Maybe the other posters are overly critical. This isn't the best solution for every scenario of polymorphic object oriented design, BUT I say it's clever and warrants at vote of 4 in my book. Thanks for the great contribution.
--- Shawn Poulson spoulson@explodingcoder.com
|
|
|
|
 |
 | Question e_roth | 12:08 10 Apr '06 |
|
 |
I was trying to implement a multimethod solution based on your ideas, but have run into some problems.
1. Your implementation uses function dsp.Arg0(), but this function was never discussed and the function name doesn't help me figure out what it is up to either.
2. Your implementation uses function dsp.UnknownArg(i, *unknown[i]), but this function was never discussed and the function name doesn't help me figure out what it is up to either.
3. What does FirstStepBase look like?
4. What does SecondStepBase look like?
I know I'm somewhat weak when it comes to template programming & template metaprogramming, but it seems like there is just to much missing out of the MultiMethodGateway section to duplicate your ideas.
|
|
|
|
 |
|
 |
Hello!
Did you see the mmdd library?
It is a free portable reusable library based on the approach described here. You can use it for free instead of trying to implement the approach by yourself.
Al last, you can use the source of the library as the complete reference.
P.S. I wonder where is a working example. It was here a year ago. I'll contact CodeProject's webmaster.
Danil
|
|
|
|
 |
|
 |
No, I didn't see your mmdd library thanks for the link! Hopefully they can find the complete example as it would be helpful as well.
Thanks, E. Roth
|
|
|
|
 |
|
 |
Contact me please, if the mmdd source will not help.
|
|
|
|
 |
 | How does your solution differ from one in "Modern C++ Design"? Grzegorz Jakacki | 18:35 11 Oct '04 |
|
 |
Could you briefly outline essential differences between your solution and a similar one given in "Modern C++ Design"?
Thanks Grzegorz
|
|
|
|
 |
|
 |
Mmm... It is a hard job to me. I'm, of course, was influenced by this book (despite bad translation in Russian). But when a read it, I've generally miss the "Multimethods" chapter . That's why my comparation maybe incomplete.
The highlights: I) All solutions that are listed in MC++D use RTTI. My solution works without it. II) The execution time in my solution depends only on arity of multimethod (that equals 2 in most cases). Solutions from MC++D (except "Constant-Time Multimethods") are more expensive by this parameter.
My solution looks like "very improved version" of "Constant-Time Multimethods". Main differences: 1) no RTTI 2) no manual "table-of-pointers" maintenance. It is done implicitly by compiler. 3) no awkward class identification (see GetClassIndexStatic())
-- Danil
|
|
|
|
 |
|
 |
Danil Shopyrin wrote: I'm, of course, was influenced by this book (despite bad translation in Russian). But when a read it, I've generally miss the "Multimethods" chapter . That's why my comparation maybe incomplete.
Then perhaps it would be best not to comment on it at all.
Danil Shopyrin wrote: I) All solutions that are listed in MC++D use RTTI. My solution works without it.
No, not all of them.
Danil Shopyrin wrote: My solution looks like "very improved version" of "Constant-Time Multimethods".
No it doesn't, you've just given us a generalized version of Andrei's generic acyclic visitor.
Danil Shopyrin wrote: Main differences: 1) no RTTI
This isn't much of a big deal, the cost is in dynamic_cast not typeid, IIRC obtaining a reference to std::type_info via typeid is an amortized constant time operation. Also you do not mention the trade-offs your solution gives...
Danil Shopyrin wrote: 2) no manual "table-of-pointers" maintenance. It is done implicitly by compiler.
In exchange for increased dependencies, an intrusive solution, code bloat, longer build times.
Danil Shopyrin wrote: 3) no awkward class identification (see GetClassIndexStatic())
Right and i must remember to override "accept/visit" for each new *visitable* sub-type i introduce into a hierarchy. You're saying that your solution isn't just as awkward and intrusive? because it is.
|
|
|
|
 |
|
 |
The idea of this article is very clear, but I got an impression that you did all you could to make reading very difficult.
Basically, I understand your article as a minor improvement upon MC++D Multimethods (Chapter 11.) I see no anathema in using RTTI or static_ and dynamic_casts, so it is not so important to me (i am sure, somebody will argue about it.) I should say that Andrei's text is much easier to understand and follow. So it would be very helpful if you have supported Andrei's terminology, and functon and variable names as much as possible.
Instead of TList, TList::Head, TList::Tail you use Shapes_, TItems, TItem, TItems::next. NullType gave a way to End_List (no definition). Why?And these terrible underscores at the ends of names..
Do you really need BoolResult as a separate class?
And you easily might be lost in a maze of FirstStep, FirsStepBase, SecondStep, etc.
Your article has a potential for use for education if a great editing work would be done.
geoyar
|
|
|
|
 |
|
 |
Hi!
geoyar wrote: The idea of this article is very clear, but I got an impression that you did all you could to make reading very difficult.
This is a very early version of the article. And maybe my writing skills was not so great at the moment of this publication. Finally, I've published an article in the IEEE Software. I hope, it’s much more readable. You can found it at doi.ieeecomputersociety.org/10.1109/MS.2006.77 But it's a paid content!
geoyar wrote: I see no anathema in using RTTI or static_ and dynamic_casts, so it is not so important to me (i am sure, somebody will argue about it.) Of course, there is no crime to use RTTI. But it's an academical challenge to implement multimethods in a pure C++, without lost of compile time type safety. My approach solves this task.
geoyar wrote: And you easily might be lost in a maze of FirstStep, FirsStepBase, SecondStep, etc.
I agree, it's a bit confusing. But these classes are needed to implement multimethods with arbitrary arity. It's also an academical challenge.
geoyar wrote: Your article has a potential for use for education if a great editing work would be done.
I really appreciate your opinion. You could contact me directly if you need any additional materials or assistance.
-- Danil
|
|
|
|
 |
|
 |
Danil Shopyrin wrote: You could contact me directly if you need any additional materials or assistance.
How? I would like to get the latest version of your article.
geoyar
|
|
|
|
 |
 | Compatible with VC++ 6 compiler? rob_hellier | 23:52 21 Jun '04 |
|
 |
Is this code compatible with the visual c++ 6 (SP5) compiler?
I have tried to create a project using this code and am having trouble getting it to compile.
most the errors are similar to:
c:\multimethods\idispatch.h(10) : error C2584: 'IDispatcher_' : direct base 'IDispatchPart_' is inaccessible; already a base of 'IDispatcher_' c:\multimethods\idispatch.h(10) : see reference to class template instantiation 'IDispatcher_' being compiled
|
|
|
|
 |
|
 |
No. I think that this code isn't compatible with MSVC++6. It is general because partial template specialization is used.
But I hope that this approach can be adapted for MSVC++6 and for other template-disabled compilers.
|
|
|
|
 |
|
 |
I've read somewhere that the microsoft compiler had somewhat "crappy" template support until version 7.0 (part of Visual Studio .NET). Unfortunately I can't remember where I read this or whether it is true or not.
You can download the .NET SDK which brings a non-optimizing C++ compiler. The optimizing version is now available at http://msdn.microsoft.com/visualc/vctoolkit2003/. I don't know if you could trick the MSVC++6 IDE to use the new compilers but you could use the editor to edit your code and then use some external method to compile your code. For example you could create a make file project in MSVC++6 and have the make file use the new compiler to compile your code. The procedure is more involved but the compiler is free (you don't have to pay for the upgrade to Visual Studio .NET).
I don't have a copy of Visual Studio 6.0 installed so I can't give you a step by step tutorial on how to make this work, but if you need help then please ask. I'll try my hardest to help.
I personally think multimethods can be a very powerful feature. I'm glad to know that they can be implemented in C++. At the moment I need more examples to really learn how to use them and when they are appropriate! I will be creating a list of powerful C++ tools/concepts and multimethods are certainly going to be added to this list!
|
|
|
|
 |
|
 |
It is true that old MS compiler has a bad support for templates. But with present-time versions the situation is better.
I think that initial question was intended especially on compiling under VC++6 compiler, not only under its IDE.
I still think that discussed approach can be adapted for VC++6 (but maybe with some featires breakdown), but I don't waste time on this job. Looks like the days of VC++6 are numbered .
The proposed library (see links above) is compilable under a set of compliers. I tried it with MSVC++ 7.1, GCC and Borland C++ compilers.
Maybe you can find more multimethods examples on the following site.
-- Danil
|
|
|
|
 |
 | Quite Slick but... Brian Ellis | 9:25 16 Jun '04 |
|
 |
This is quite a slick solution, but I would still try to design my framework to have a more specialized logical intermediate (such as CRegion for shapes) before I resorted to an over-generalization like this. This dispatcher pattern would excel in performance, but it would be more difficult to maintain. I'd just as soon aviod the problem of multimethods in the design of my framework.
|
|
|
|
 |
|
 |
You can use a mixed solution: - use "CRegion" as a main solution; - pointwise insert specialized implementations.
So you can improve performance of your program. Using some statistics you can determine "most frequently used intersections" and specialize them.
In some applications multimethods connot be avoided .
|
|
|
|
 |
 | Porting Anonymous Fred | 12:04 12 Jun '04 |
|
 |
This code is written in the Boost programming language, right? Can you port it to C++?
Thnx, Fred
|
|
|
|
 |
|
|