Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Multimethods idioms and library support

Reply
Thread Tools

Multimethods idioms and library support

 
 
James Kanze
Guest
Posts: n/a
 
      02-23-2011
On Feb 22, 1:09 pm, itaj sherman <(E-Mail Removed)> wrote:
> Is there any concensus idiom on how to code with multimethods?
> How would you go about coding them today?


> There's the multi-dispatch idiom: For each override type of the first
> parameter. declare a different virtual function to dispatch the second
> parameter.http://en.wikipedia.org/wiki/Multiple_dispatch
> But it requires that the definition of every polymorphic class of one
> parameter #includes the definition of all polymorphic types of the
> following parameter. This makes the code messy, and not scalable due
> to circular dependencies between modules.


The classical solution is:

class Derived1;
class Derived2;
// ...

class Base
{
protected:
virtual void doSomething(Derived1&);
virtual void doSomething(Derived2&);
// ...
public:
virtual void doSomething(Base& other);
};

class Derived1 : public Base
{
protected:
virtual void doSomething(Derived1& other)
{
// Derived1, Derived1 handling...
}
virtual void doSomething(Derived2& other)
{
// Derived1, Derived2 handling...
}
public:
virtual void doSomething(Base& other)
{
other.doSomething(*this);
}
};

If the hierarchy is closed (all of the derived classes
identifiable, and managed by a single entity), and not too big,
this is fine. If you want to be able to easily expand the
hierarchy, then it is far from ideal, and anything over four or
five derived classes quickly becomes overwhelming.

Multiple dispatch on open or large hierarchies is problematic in
general. For n derived classes, you need n^2 functions
(regardless of how the dispatch works), and when you add
a derived class, you have to define its interactions with all of
the existing classes. It's best avoided, but it is possible to
make it work: you need something like a
std::map<std:air<std::type_index, std::type_index>,void (*)
(Base&, Base&)>
, where void Base::doSomething(Base& other) isn't virtual, but
looks up the actual (free) function to call in the map. Getting
the map initialized with all of the necessary functions is
non-trivial, but if you can provide a default Base&, Base&
functionality, and only need a few specific overrides, it might
be OK.

> I had to use them for a some medium size hirarchy and this
> idiom became quite a bother. Not to mention that users coult
> not add their types becuase this idiom is circular dependant.


> I'm more interested in some library that would support this in
> a more generic way. I suppose any such library would need
> some compiler specific code for each compiler that it
> supports, but the API should be the same.


If you want an open hierarchy which supports all possible
combinations, the problem isn't the compiler; it's how to
provide all of the necessary functions. Each new class has to
provide all of the functions for it and all of the existing
classes. This quickly gets out of hand.

--
James Kanze
 
Reply With Quote
 
 
 
 
Larry Evans
Guest
Posts: n/a
 
      02-23-2011
On 02/22/11 22:36, Larry Evans wrote:
> On 02/22/11 21:04, Larry Evans wrote:
>> On 02/22/11 16:08, Larry Evans wrote:
>> [snip]
>>> Let me see if I can implement this with reifier_visitor.
>>> I'll get back to you.
>>>

>> After some corrections to:
>>
>> replace_source_with_target_ptr.hpp
>>
>> got the attached to run and print:
>>
>> Lion jumps on Girrafe and bites its ass.
>>
>> However, when the #if on line 188 is switch, I get
>> very obscure compile-time error messages. So, it
>> can't handle concrete references. It apparently
>> can only handle abstract references.
>>
>> I'll see if I can correct that.
>>

> The compiler error messages state that:
>
> hosts_concrete<Lion>
>
> is an incomplete type in reifier_visitor.hpp line 209.
> This is where there's a hosts_concrete<HeadAbstract>
> and HeadAbstract is Lion which is not abstract.
> hosts_concrete is supposed to be specialized on
> the abstract class to return the set of concrete
> classes for that abstract class. Since Lion already
> is an concrete class, there's no need to specialize
> hosts_concrete on it; hence, the error.
>
> Will think about solution.

One solution is to define a new metafunction in reifier_visitor.hpp:

template
< typename HostConcrete
>

struct host_abstract
/**@brief
* Metafunction returning Abstract class
* of which HostConcrete is a member.
* IOW, does inverse of hosts_concrete.
*/
{
typedef
HostConcrete
type
//By default, HostConcrete is member
//of a type hierarchy starting at
//HostConcrete.
;
};

and them modify the argument, HeadAbstract, to hosts_concrete to
super of reifier_visitor by host_abstract<HeadAbstract>::type.
That converts any concrete type back to its abstract super.
Of course that's less than ideal because it requires
specializations of host_abstract to map all the derived classes
back to their abstract classes and causes mover virtual
function calls to undo what was just done by host_abstract.

Will upload the modified reifier_visitor.hpp to sandbox.
The modified driver, with all the host_abstract specializations,
is attached.

-Larry


 
Reply With Quote
 
 
 
 
Stuart Redmann
Guest
Posts: n/a
 
      02-23-2011

On 22 Feb., itaj sherman wrote:
> You replace a dynamic_cast if-else-if list with a virtual function
> Prey::dispatchMe. This save the detailing on 1 parameter. I'm not sure
> it would be so helpful for 3 or more virtual parameters.
> Also the more internal animal_kingdom module contains Prey which has
> to know (forward-dec) the name of CarnivourDispatch which in turn has
> to know all polymorphic types under Prey. As it is, it breaks the
> encapsulation of the module, becuase user must make it somehow aware
> of his different derivations.


There is no possible way that Double Dispatch could work without some
entity knowing all sub-classes that are involved in Double Dispatch.
The way I have shown is probably far from perfect, but it shows that
this problem can be solved by C++ without resorting to dynamic_casts.

> I think that would be especially more
> problematic if there was another user-code adding more derivations,
> but that would want to use some of these derivations too.


Well, if two users A and B add different objects, someone has to
figure what should happen if objects from user A meet objects of user
B.


SCNR:
#include <iostream>

// We want to be able to dynamically dispatch three different
// base classes, Prey, Carnivour, and Location, with n
// sub-classes. Since Double Dispatch uses vtables, we have to
// have a class with n^2 entries in its vtable. This class contains
// the actual algorithmic part (in our scenario this class will
// be the Carnivour class).
// To be able to implement Double Dispatch, we have to stuff
// one additional class with a vtable of n entries (Location).

///////////////////////////////////////////////////////////

class CarnivourDispatch;
class Location;

class Prey
{
public:
virtual void dispatchMe (CarnivourDispatch*,Location&) = 0;
};

///////////////////////////////////////////////////////////

class Location;

class Carnivour
{
public:
virtual void hunt (Prey& prey, Location& location) = 0;
};

///////////////////////////////////////////////////////////

// CarnivourDispatch needs to know all derived classes, both
// prey and locations.
class Plains;
class Ocean;
class Gazelle;
class Giraffe;
class Gnu;

class CarnivourDispatch : public Carnivour
{
public:
// This starts the double dispatch.
virtual void hunt (Prey& prey, Location& location)
{
prey.dispatchMe (this, location);
}
virtual void huntPreyLocation (Gazelle&, Plains&) = 0;
virtual void huntPreyLocation (Giraffe&, Plains&) = 0;
virtual void huntPreyLocation (Gnu&, Plains&) = 0;
virtual void huntPreyLocation (Gazelle&, Ocean&) = 0;
virtual void huntPreyLocation (Giraffe&, Ocean&) = 0;
virtual void huntPreyLocation (Gnu&, Ocean&) = 0;
};

///////////////////////////////////////////////////////////

template<class t_ImplementationClass>
class PreyDispatchHelper : public Prey
{
public:
void dispatchMe (CarnivourDispatch* Hunter, Location& location)
{
location.dispatchMe (Hunter, *static_cast<t_ImplementationClass*>
(this));
}
};

class Giraffe : public PreyDispatchHelper<Giraffe>
{
};

class Gazelle : public PreyDispatchHelper<Gazelle>
{
};

class Gnu : public PreyDispatchHelper<Gnu>
{
};

//////////////////////////////////////////////////////////////////////////
// We need to know all derived classes of Prey at compile-time.

// Forward declaration of all Prey-derived classes.
class Gazelle;
class Giraffe;
class Gnu;

template<class t_Base1, class t_Base2>
class TMultipleInheritance : public t_Base1, public t_Base2
{
public:
typedef t_Base1 TBase1;
typedef t_Base2 TBase2;
};

class NullPrey
{};

class PreyEnum : public TMultipleInheritance
<
Gnu,
TMultipleInheritance
<
Giraffe,
TMultipleInheritance
<
Gazelle, NullPrey
>
>
>

{
};

// LocationHelper2 adds the virtual methods
// virtual void dispatchMe (CarnivourDispatch*, Gazelle&) = 0;
// virtual void dispatchMe (CarnivourDispatch*, Gnu&) = 0;
// and so on to the Location interface.
template<class t_DummyArgument = int, class t_PreyEnum = PreyEnum>
class LocationHelper2 : public LocationHelper2<t_DummyArgument,
typename t_PreyEnum::TBase2>
{
public:
typedef LocationHelper2<t_DummyArgument, typename
t_PreyEnum::TBase2> TBaseLocationHelper2;
using TBaseLocationHelper2::dispatchMe;
virtual void dispatchMe (CarnivourDispatch* Hunter,typename
t_PreyEnum::TBase1& prey) = 0;
};

template<class t_DummyArgument>
class LocationHelper2<t_DummyArgument, NullPrey>
{
public:
typedef NullPrey TBaseLocationHelper2;

// We need this as base for the using-declaration in the
// non-specialized template.
void dispatchMe (){}
};

// We could as well drop this class, but it makes forward
// declarations much easier.
class Location : public LocationHelper2<>
{
};

////////////////////////////////////////////////////////////
// Implementation helper of the virtual methods of Location
template<class t_ImplemenationClass, class t_PreyEnum = PreyEnum>
class LocationDispatchHelper2 : public
LocationDispatchHelper2<t_ImplemenationClass, typename
t_PreyEnum::TBase2>
{
protected:
typedef LocationDispatchHelper2<t_ImplemenationClass, typename
t_PreyEnum::TBase2> TBaseLocationDispatchHelper2;
public:
using TBaseLocationDispatchHelper2::dispatchMe;
virtual void dispatchMe (CarnivourDispatch* Hunter,typename
t_PreyEnum::TBase1& prey)
{
Hunter->huntPreyLocation (prey,
*static_cast<t_ImplemenationClass*> (this));
}
};

template<class t_ImplemenationClass>
class LocationDispatchHelper2<t_ImplemenationClass, NullPrey> : public
Location
{
protected:
typedef Location TBaseLocationDispatchHelper2;
};


// Now we can easily add our locations.
class Plains : public LocationDispatchHelper2<Plains>
{
};

class Ocean : public LocationDispatchHelper2<Ocean>
{
};

class Lion: public CarnivourDispatch
{
protected:
virtual void huntPreyLocation (Gazelle& gazelle, Plains& plains)
{
std::cout << "Lion jumps on gazelle and bites its neck in the
plains." << std::endl;
}

virtual void huntPreyLocation (Giraffe& giraffe, Plains& plains)
{
std::cout << "Lion bites giraffe's ass in the plains" <<
std::endl;
}

virtual void huntPreyLocation (Gnu& gnu, Plains& plains)
{
std::cout << "Lion rides gnu to death in the plains" << std::endl;
}

virtual void huntPreyLocation (Gazelle&, Ocean&)
{
std::cout << "Lion cooks gazelle in Pacific Ocean." << std::endl;
}
virtual void huntPreyLocation (Giraffe&, Ocean&)
{
std::cout << "Lion runs over giraffe with jet skies." <<
std::endl;
}

virtual void huntPreyLocation (Gnu&, Ocean&)
{
std::cout << "Lion launches torpedo at gnu." << std::endl;
}
};


//main.cpp
int main()
{
Carnivour* carnivour = new Lion;
Location* location = new Plains;
Location* location2 = new Ocean;
Prey* prey1 = new Gazelle;
Prey* prey2 = new Giraffe;
Prey* prey3 = new Gnu;
carnivour->hunt (*prey1, *location);
carnivour->hunt (*prey2, *location);
carnivour->hunt (*prey3, *location);
carnivour->hunt (*prey1, *location2);
carnivour->hunt (*prey2, *location2);
carnivour->hunt (*prey3, *location2);

return 0;
}


Regards,
Stuart
 
Reply With Quote
 
Paul
Guest
Posts: n/a
 
      02-23-2011

"itaj sherman" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> On Feb 23, 12:08 am, Larry Evans <(E-Mail Removed)> wrote:
>> On 02/22/11 15:23, itaj sherman wrote:
>>

You will never be a man.

 
Reply With Quote
 
itaj sherman
Guest
Posts: n/a
 
      02-23-2011
On Feb 23, 2:08 pm, James Kanze <(E-Mail Removed)> wrote:

> Multiple dispatch on open or large hierarchies is problematic in
> general. For n derived classes, you need n^2 functions
> (regardless of how the dispatch works), and when you add
> a derived class, you have to define its interactions with all of
> the existing classes. It's best avoided, but it is possible to
> make it work: you need something like a
> std::map<std:air<std::type_index, std::type_index>,void (*)
> (Base&, Base&)>
> , where void Base::doSomething(Base& other) isn't virtual, but
> looks up the actual (free) function to call in the map. Getting
> the map initialized with all of the necessary functions is
> non-trivial, but if you can provide a default Base&, Base&
> functionality, and only need a few specific overrides, it might
> be OK.
>


>
> If you want an open hierarchy which supports all possible
> combinations, the problem isn't the compiler; it's how to
> provide all of the necessary functions. Each new class has to
> provide all of the functions for it and all of the existing
> classes. This quickly gets out of hand.
>



Ofcourse for k virtual parameters, the number of possible different
overrides is n^k. But so is the number of possible different static
overloads for a regular n-ary function in c++. But usually there are
some generalizations that are done on the possibilities that
practically cut them to around log(n)^k to n*log(n)^k sets and subsets
(just top of my head estimation). In both cases that should be done
using the hierarchy and templates.

* For conviniency I'll base on the hipothetic language feature. But
what I mean is that it should be possible to create a library that
would support the same general constructs, just instead of syntax
you'll have to define classes and static variable of the library type.

template< typename C, typename P >
hunt_impl( C const&, P& )
{
....
}

template< typename C, typename Horse1, typename Horse2 >
hunt_impl( C const& carnivour, Interbreed< Horse1, Horse2 >& prey )
{
...
}

void hunt( override< Bear > const& carnivour, override< Prey >& Prey )
{
return hunt_impl( carnivour, prey );
}

//the following syntax override 2 cases:
void hunt( override< Lion > const& carnivour, override< Giraffe,
Gazelle >& prey )
{
return hunt_impl( carnivour, prey );
}

void hunt( override< Anaconda > const& carnivour, override< Giraffe,
Gazelle >& prey )
{
return hunt_impl( carnivour, prey );
}


Now if anyone came to the conclusion that they really have to specify
all different combinations, in order to describe their problem
correctly, then so be it - such syntax can only make it easier, not
worse.

itaj
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-23-2011
On Feb 23, 4:07 pm, itaj sherman <(E-Mail Removed)> wrote:
> On Feb 23, 2:08 pm, James Kanze <(E-Mail Removed)> wrote:
> > Multiple dispatch on open or large hierarchies is problematic in
> > general. For n derived classes, you need n^2 functions
> > (regardless of how the dispatch works), and when you add
> > a derived class, you have to define its interactions with all of
> > the existing classes. It's best avoided, but it is possible to
> > make it work: you need something like a
> > std::map<std:air<std::type_index, std::type_index>,void (*)
> > (Base&, Base&)>
> > , where void Base::doSomething(Base& other) isn't virtual, but
> > looks up the actual (free) function to call in the map. Getting
> > the map initialized with all of the necessary functions is
> > non-trivial, but if you can provide a default Base&, Base&
> > functionality, and only need a few specific overrides, it might
> > be OK.


> > If you want an open hierarchy which supports all possible
> > combinations, the problem isn't the compiler; it's how to
> > provide all of the necessary functions. Each new class has to
> > provide all of the functions for it and all of the existing
> > classes. This quickly gets out of hand.

>
> Ofcourse for k virtual parameters, the number of possible different
> overrides is n^k. But so is the number of possible different static
> overloads for a regular n-ary function in c++. But usually there are
> some generalizations that are done on the possibilities that
> practically cut them to around log(n)^k to n*log(n)^k sets and subsets
> (just top of my head estimation).


Hopefully. At least if n is large.

> In both cases that should be done using the hierarchy and
> templates.


> * For conviniency I'll base on the hipothetic language feature. But
> what I mean is that it should be possible to create a library that
> would support the same general constructs, just instead of syntax
> you'll have to define classes and static variable of the library type.


It certainly isn't difficult to come up with a solution for
a given hierarchy. I'm not sure about a generic solution;
I haven't given it a try, but it seems like it should be
possible. The problem is to make the lookup as effective as
possible: if you require n^k functions, each for the most
derived class, you can use a hash table of some sort on the two
(or more) type_info's. If you don't, it's more difficult, since
type_info doesn't give you information about the base classes.
And you need to specify some sort of ordering: either first
match, and insist that the client codinsert in the correct
order (with the most specific functions where they will be found
first), or best match, and you have to define "best", evaluate
all matches, and define behavior in case of ambiguity. It's not
an easy task.

--
James Kanze
 
Reply With Quote
 
itaj sherman
Guest
Posts: n/a
 
      02-23-2011
On Feb 23, 6:26*pm, James Kanze <(E-Mail Removed)> wrote:
> On Feb 23, 4:07 pm, itaj sherman <(E-Mail Removed)> wrote:
>


>
> > In both cases that should be done using the hierarchy and
> > templates.
> > * For conviniency I'll base on the hipothetic language feature. But
> > what I mean is that it should be possible to create a library that
> > would support the same general constructs, just instead of syntax
> > you'll have to define classes and static variable of the library type.

>
> It certainly isn't difficult to come up with a solution for
> a given hierarchy. *I'm not sure about a generic solution;
> I haven't given it a try, but it seems like it should be
> possible. *The problem is to make the lookup as effective as
> possible: if you require n^k functions, each for the most
> derived class, you can use a hash table of some sort on the two
> (or more) type_info's. *If you don't, it's more difficult, since
> type_info doesn't give you information about the base classes.


I wouldn't mind if it was intrusive. I.e. for every class in the
hierarchy the user would have to state the list of base classes.

> And you need to specify some sort of ordering: either first
> match, and insist that the client codinsert in the correct
> order (with the most specific functions where they will be found
> first), or best match, and you have to define "best", evaluate
> all matches, and define behavior in case of ambiguity. It's not
> an easy task.
>


I thought though there might have been such a library already
somewhere.

itaj
 
Reply With Quote
 
ld
Guest
Posts: n/a
 
      02-24-2011
On Feb 23, 11:49*am, itaj sherman <(E-Mail Removed)> wrote:
> On Feb 23, 12:30*pm, ld <(E-Mail Removed)> wrote:
>
>
>
> > If you can afford C instead of C++, you can read papers and use the
> > COS lib

>
> Well, that is basically my idea. Although when you work in C, the
> language doesn't offer almost any of the features of this object
> system (classes, inheritance, function resolution, templates?), so it
> seems right to use it for everything. Unlike C, C++ has core features
> for everything that this library does, except multimethods.


Well, multimethod is only the beginning of the game. The true feature
of COS is fast message multi-dispatch and generic delegation (message
forwarding) which require to transparently (on-the-fly) to marshall
(caller site) and un-marshall (callee site) the arguments of the
multimethods.

Coming back to C++, the problem with multimethod is that C++ support
multiple inheritance. It's a killer for generic manipulation of
function pointers and makes it's object model particularly complex
(and not easily extensible). The only way I found to partially solve
the problem is to build on-the-fly a (templated) pimpl object specific
to each multimethod with functor capabilities (in the C++ sense). The
idea is to shift problematic generic pointer to functions to
manageable generic pointer to simple objects. The rest relies more on
highly specialized (very fast) hash tables and algebraic properties of
group generator (for keys). On the other hand, generic delegation is
beyond what C++ can do because it already has too many core features
(main advantage of C).

> Principally I want to use the core features of C++ to write my code. I
> only need a library that supports multimethods, that would extend them
> over hirarchies that are already written with core C++ features. So in
> that sense, I don't think I can afford transforming all my hirarchies
> into any library. That would beat the purpose of finding the crispiest
> idiom for multimethods.


I understand your point. My conclusion from trying to port COS to C++
(still a dream) is that C++ needs native support for multimethods (cf.
BS paper for the best proposal I know) because multiple inheritance is
itself a core feature of C++. Otherwise, either code complexity,
compilation time or runtime speed will strongly suffer from
multimethods, if I compare to COS which is extremely simple for its
users and very fast.
 
Reply With Quote
 
Larry Evans
Guest
Posts: n/a
 
      02-25-2011
On 02/23/11 07:21, Larry Evans wrote:
> On 02/22/11 22:36, Larry Evans wrote:
>> On 02/22/11 21:04, Larry Evans wrote:
>>> On 02/22/11 16:08, Larry Evans wrote:
>>> [snip]
>>>> Let me see if I can implement this with reifier_visitor.
>>>> I'll get back to you.
>>>>
>>> After some corrections to:
>>>
>>> replace_source_with_target_ptr.hpp
>>>
>>> got the attached to run and print:
>>>
>>> Lion jumps on Girrafe and bites its ass.
>>>
>>> However, when the #if on line 188 is switch, I get
>>> very obscure compile-time error messages. So, it
>>> can't handle concrete references. It apparently
>>> can only handle abstract references.
>>>
>>> I'll see if I can correct that.
>>>

>> The compiler error messages state that:
>>
>> hosts_concrete<Lion>
>>
>> is an incomplete type in reifier_visitor.hpp line 209.
>> This is where there's a hosts_concrete<HeadAbstract>
>> and HeadAbstract is Lion which is not abstract.
>> hosts_concrete is supposed to be specialized on
>> the abstract class to return the set of concrete
>> classes for that abstract class. Since Lion already
>> is an concrete class, there's no need to specialize
>> hosts_concrete on it; hence, the error.
>>
>> Will think about solution.

> One solution is to define a new metafunction in reifier_visitor.hpp:
>
> template
> < typename HostConcrete
> >

> struct host_abstract
> /**@brief
> * Metafunction returning Abstract class
> * of which HostConcrete is a member.
> * IOW, does inverse of hosts_concrete.
> */
> {
> typedef
> HostConcrete
> type
> //By default, HostConcrete is member
> //of a type hierarchy starting at
> //HostConcrete.
> ;
> };
>
> and them modify the argument, HeadAbstract, to hosts_concrete to
> super of reifier_visitor by host_abstract<HeadAbstract>::type.
> That converts any concrete type back to its abstract super.
> Of course that's less than ideal because it requires
> specializations of host_abstract to map all the derived classes
> back to their abstract classes and causes mover virtual
> function calls to undo what was just done by host_abstract.
>
> Will upload the modified reifier_visitor.hpp to sandbox.
> The modified driver, with all the host_abstract specializations,
> is attached.
>
> -Larry
>

A better solution has just been uploaded. It uses boost::bind to 1st
bind the concrete arguments before then calling
reify_apply<reifier_visitor>(bound_f,...) to reify the remaining
abstract argument and then call the partialy bound function with those
args. The avoids the need for specializing head_abstract on each
concrete class.

The output of the currently uploaded predator_prey.cpp is:

Lion jumps on Girrafe and bites its ass.
Anaconda ignores Girraffe.
Bear mauls Gazelle
Lion jumps on Gazelle and bites its neck.

 
Reply With Quote
 
Larry Evans
Guest
Posts: n/a
 
      02-25-2011
On 02/22/11 16:31, itaj sherman wrote:
> On Feb 23, 12:08 am, Larry Evans <(E-Mail Removed)> wrote:
>> On 02/22/11 15:23, itaj sherman wrote:
>>
>>
>>
>>> On Feb 22, 7:02 pm, Larry Evans <(E-Mail Removed)> wrote:

>> [snip]
>>>> If g++ is available, you can use the -std=gnu++0x option to enable
>>>> variadic templates and then use the code demonstrated the
>>>> test driver here:

>>
>>>> http://svn.boost.org/svn/boost/sandb...adic_templates
>>>> /libs/composite_storage/sandbox/pack
>>>> /one_of_multiple_dispatch.test.cpp

>> [snip]
>>> So, after I managed to check out the code, it makes more sense.

>>
>>> But, tell me if the following is correct:
>>> using this library on a hirarchy of classes and a certain multimethod.
>>> - I have to collect the declarations of all different overrides in the
>>> same functor class, like functor3 and functor_any do.

>>
>> Yes.
>>
>>> - I have to collect all the concrete classes of the hirarchy in one
>>> list, as under hosts_concrete<>

>>
>> Yes. Otherwise, how could you define the abstract visitor class in an
>> extensible way?
>>

>
> I suppose the general idea could maybe be to hold some static variable
> of dispatch matrices and edit it in constructors of other static
> variables.


Not AFAICT. What's needed is a specialization of hosts_concrete for
each abstract class. For example, see the current predator_prey.cpp:

http://svn.boost.org/svn/boost/sandb...dator_prey.cpp

There's two specializations of hosts_concrete:

template<>
struct hosts_concrete
< Predator_abstract
>

: bmpl:ackage
< Lion
, Bear
, Anaconda
>

{
};

template<>
struct hosts_concrete
< Prey_abstract
>

: bmpl:ackage
< Gazelle
, Girrafe
>

{
};

Each argument after the functor argument to
reify_apply<reifier_visitor> must be a HostAbstract& for some
HostAbstract such that hosts_concrete<HostAbstract> is defined.

> But that is only good for pure runtime dispatch, from what I figured,
> your library is about compile type dispatching,


No. In each case, reifier_visitor and reifier_switch, dispatching is
done a runtime:

reifier_visitor:
This uses the classic visitor pattern where the abstract visit
methods occur in the superclasses of reifier_visitor. These
reifier_visitor superclasses are the:

reifier_visit_concrete_pack
< ReifierVisitor
, ConcretePkg
, ConcreteHead
, ConcreteTail...
>


classes. Each defines the visit method for ConcreteHead. Since
they are inherited one after the other, ultimately there's one
visit method for each class in
hosts_concrete<HostAbstract>::type.

reifier_switch:
For this method, there's no need for something like hosts_concrete
since the set of concrete classes, {C1,C2,...,Cn} are explicitly
listed in the template arguments to:

composite_storage:ack::container
< one_of_maybe
, Index0
, C1,C2,...,Cn
>.


In place of the virtual function calls to reify the abstract class
into its concrete class, the reifier_switch uses a switch
statement on the one_of_maybe which() result.

> so maybe it doesn't have enough in common with the solution I'm
> looking for. I'm not sure.


I hope the above clarifies the issue.

[snip]
>
> Mainly I deduced my conclusion from the test code, seeing there how
> you used reifier_visitor and reifier_apply. I just gazed at the
> code of reifier_visitor, I don't know how it works at all, neither
> the difference with reifier_switch.


The reify_apply trades calls with the reifier_XXX, where XXX in
{switch,visitor} n times, where n is the number of arguments to be
applied to the reify_apply functor argument. During each such trade,
the reifier reifies the next abstract argument to produce the concrete
counterpart, then calls reify_apply_impl with the modified argument
list. When reify_apply_impl is called with the complete concretized
argument list(see reify_apply.hpp:6, it applies the functor to the
concrete argument list.

Does that make things clearer?

HTH.

-regards,
Larry

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Idioms and Anti-Idioms Question Ben Charrow Python 11 07-04-2009 12:08 AM
multimethods decorator Gerardo Herzig Python 4 10-11-2007 08:21 PM
Shortening common idioms: bus assignment and 'prev' generation Eli Bendersky VHDL 9 04-07-2006 08:06 AM
Decorators for multimethods Roman Suzi Python 0 12-10-2004 08:52 PM
decorators and multimethods Michele Simionato Python 8 08-19-2004 09:13 AM



Advertisments