An acyclic visitor that climbs up a class hierarchy to find a matching method to call.
The code is written such that you only have to include the header file found in the
include
directory. The relevant classes are in the cav
namespace, so for demonstration
purposes it's probably the best to just use that entire namespace:
#include "climbing-acyclic-visitor.hpp"
using namespace cav;
For a class to be visitable, it must declare a "parent visitable type" parent_visitable
(to which it must be castable, too) and inherit from the is_visitable
template class,
providing itself as template parameter.
So to declare the root of a hierarchy as being visitable, the following is sufficient:
class root : /* possibly other superclasses */ public is_visitable<root>
{
public:
using parent_visitable = base_visitable;
// ...
};
In this case root
implicitely inherits from its "parent visitable type" through the
is_visitable
template class, so there is no need to directly derive from base_visitable
.
The class root
now has an method accept
through which it can be fed to a visitor class.
To declare a sub class as being visitable, there is the additional "burden" of explicitely
declaring the accept method to use the correct accept method from the is_visitable
parent
(and not the one from the "real" parent class):
class sub : public root, public is_visitable<sub>
{
public:
using parent_visitable = root;
void accept(base_visitor & visitor)
{
is_visitable<sub>::accept(visitor);
}
};
Now, a visitor may define a special behaviour for the sub
class. The advantage compared to
a regular acyclic visitor is, that the visitor doesn't NEED to define a special behaviour. In case
no special behaviour is defined, an object of type sub
is handled the same way as an object of
type root
.
Further subclassing works analogous.
Of course, one can also define a "regular" sub class of root
:
class regular_sub : public root
{
// ... anything ...
};
regular_sub
now inherits the accept
method from its parent, but a visitor may not define special
behaviour on regular_sub
.
Further subclassing regular_sub
is a bit special: One must not declare regular_sub
as parent_visitable
(because it's not a "real" visitable) but instead use the next parent that really is a "visitable":
class sub_regular_sub : public regular_sub, public is_visitable<sub_regular_sub>
{
public:
using parent_visitable = root; // Important to choose the right class here!
void accept(base_visitor & visitor)
{
is_visitable<sub_regular_sub>::accept(visitor);
}
};
One additional note here: One can also declare a sub class as being visitable, but "skipping" some parent visitable classes or start a complete new visitable hierarchy:
class subsub : public sub, public is_visitable<subsub>
{
public:
using parent_visitable = root; // Skip sub, i.e. don't let a visitor threat this as a sub.
void accept(base_visitor & visitor)
{
is_visitable<subsub>::accept(visitor);
}
};
class subsub_new_hierarchy : public sub, public is_visitable<subsub_new_hierarchy>
{
public:
using parent_visitable = base_visitable; // Threat this as the root of a visitable hierarchy.
void accept(base_visitor & visitor)
{
// But must resolve ambiguities!
is_visitable<subsub_new_hierarchy>::accept(visitor);
}
};
To define a visitor, one must derive from the template class can_visit
, giving the class
the visitor is able to visit as template parameter. One must then provide an implementation
of the visit
method with one parameter (whose type is the same as the template parameter):
class some_visitor : public can_visit<root>,
public can_visit<sub_regular_sub>,
public can_visit<sub>
{
public:
void unknown_visitable(base_visitable & bv)
{
// handle it somehow
std::cout << "unknown" << std::endl;
}
void visit(root & r)
{
// do something ...
std::cout << "Is root" << std::endl;
}
void visit(sub & s)
{
// do something ...
std::cout << "Is sub" << std::endl;
}
void visit(sub_regular_sub & srs)
{
// do something different ...
std::cout << "Is sub_regular_sub" << std::endl;
}
};
The let a visitor visit a visitable, the visitable must accept the visitor:
some_visitor my_visitor;
root r;
sub s;
subsub subs;
regular_sub rs;
sub_regular_sub subrs;
subsub_new_hierarchy subs_nh;
r.accept(my_visitor); // Is root
s.accept(my_visitor); // Is sub
subs.accept(my_visitor); // Is root
rs.accept(my_visitor); // Is root
subrs.accept(my_visitor); // Is sub_regular_sub
subs_nh.accept(my_visitor); // unknown