已经知道,使用 Visitor有这样那样的不便。Loki为了避免这些提供了两个方案,之一就是Acyclic的类库化。
class
BaseVisitor
... {
public:
virtual ~BaseVisitor()...{};
} ;
template < class T,typename R = void >
class Visitor
... {
public:
typedef R ReturnType;
virtual ReturnType Visit(T&) = 0;
} ;
... {
public:
virtual ~BaseVisitor()...{};
} ;
template < class T,typename R = void >
class Visitor
... {
public:
typedef R ReturnType;
virtual ReturnType Visit(T&) = 0;
} ;
你的访问者必须继承这两个类,具体的情况可能是:
class
MyVisitor
: public BaseVisitor
, public Visitor < A >
, public Visitor < B >
... {
public:
void Visit(A&); //visit A
void Visit(B&); //visit B
}
: public BaseVisitor
, public Visitor < A >
, public Visitor < B >
... {
public:
void Visit(A&); //visit A
void Visit(B&); //visit B
}
使用起来很简单,记得继承和根据自己的特定类编写特定Visit方法就可以了。对于Visitable:
template
<
typename R
=
void
>
class BaseVisitable
... {
public:
typedef R ReturnType;
virtual ~BaseVisitable()...{};
virtual ReturnType Accept(BaseVisitor&) = 0;
protected:
template <class T>
static ReturnType AcceptImpl(T& visited,BaseVisitor& guest)
...{
if (Visitor<T>* p = dynamic_cast<Visitor<T>*>(&guest))
...{
return p->Visit(visited);
}
return ReturnType();
}
} ;
#define DEFINE_VISITABLE()
virtual ReturnType Accept(BaseVisitor & guest)
... {return AcceptImpl(*this,guest);}
class BaseVisitable
... {
public:
typedef R ReturnType;
virtual ~BaseVisitable()...{};
virtual ReturnType Accept(BaseVisitor&) = 0;
protected:
template <class T>
static ReturnType AcceptImpl(T& visited,BaseVisitor& guest)
...{
if (Visitor<T>* p = dynamic_cast<Visitor<T>*>(&guest))
...{
return p->Visit(visited);
}
return ReturnType();
}
} ;
#define DEFINE_VISITABLE()
virtual ReturnType Accept(BaseVisitor & guest)
... {return AcceptImpl(*this,guest);}
你已经猜到,你的Visitable必须继承BaseVisitable,但是,Visit是要动态转换的,这个调用地方必须在子类中,放在基类中动态转换没有任何意义。为了提供侵入用户类的方便手段,loki定义了DEFINE_VISITABLE()宏,如果你对MFC有一定了解的话,你一定会觉得这种手法很熟悉。
当然,侵入宏只是为了只是动态转换,如果你需要,自己写也没什么,你还可以获得应有的自由度。
这样,一般的使用BaseVisitable的方法是:
class
A :
public
BaseVisitable
<>
... {
public:
DEFINE_VISITABLE()
} ;
... {
public:
DEFINE_VISITABLE()
} ;
把宏替换为它的定义代码,修改Accept的实现就可以自定义AcceptImpl的行为。