使用C ++模板实现访问者模式

Implementing the visitor pattern using C++ Templates

我一直试图通过使用C++模板来实现访问者模式来减少代码中的样板数量。到目前为止,我想到了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class BaseVisitor {
public:
    virtual ~BaseVisitor() {}
};

template<typename T>
class Visitor : public BaseVisitor {
public:
    virtual void visit(T& /* visitable */) = 0;
};

template<typename Derived>
class Visitable {
public:
    void accept(Visitor<Derived>& visitor) {
        visitor.visit(static_cast<Derived&>(*this));
    }
};

visitable的每个子类如下:

1
2
class Mesh : public Object, public Visitable<Mesh> {};
class Text : public Object, public Visitable<Text> {};

最后,访客看起来是这样的:

1
class Renderer : public Visitor<Mesh>, public Visitor<Text> {}

到现在为止,一直都还不错。。。现在的问题是:

1
2
3
4
5
6
7
for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
    Object& object = static_cast<Object&>(*it);
    if(pre_visit(object)) {
        object.accept(this); ///Erm, what do I cast to??
        post_visit(object);
    }
}

我需要以某种方式强制转换为visitable,以便调用accept(),但显然我不知道什么是t。或者,我不能向可见模板添加虚拟accept(),因为我不知道它应该采用什么参数。

任何C++模板大师都知道如何做这个工作吗?


这可以用变量模板在C++ 11中完成。从皮特的回答继续:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Visitor template declaration
template<typename... Types>
class Visitor;

// specialization for single type    
template<typename T>
class Visitor<T> {
public:
    virtual void visit(T & visitable) = 0;
};

// specialization for multiple types
template<typename T, typename... Types>
class Visitor<T, Types...> : public Visitor<Types...> {
public:
    // promote the function(s) from the base class
    using Visitor<Types...>::visit;

    virtual void visit(T & visitable) = 0;
};

template<typename... Types>
class Visitable {
public:
    virtual void accept(Visitor<Types...>& visitor) = 0;
};

template<typename Derived, typename... Types>
class VisitableImpl : public Visitable<Types...> {
public:
    virtual void accept(Visitor<Types...>& visitor) {
        visitor.visit(static_cast<Derived&>(*this));
    }
};

Visitable的子类:

1
2
class Mesh : public Object, public VisitableImpl<Mesh, Mesh, Text> {};
class Text : public Object, public VisitableImpl<Text, Mesh, Text> {};

a Visitor子类:

1
class Renderer : public Visitor<Mesh, Text> {};

不清楚您的Scene容器的value_type是什么,但您需要获得一个指向Visitable的引用或指针,在该引用或指针上调用accept

1
2
3
4
5
6
7
for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
    Visitable<Mesh, Text>& object = static_cast<Visitable<Mesh, Text>&>(*it);
    if(pre_visit(object)) {
        object.accept(*this);
        post_visit(object);
    }
}


除了允许任意访问者删除访问者之外,您的basevisitor不为您做任何事情。相反,您希望为访问者提供一个基类,它提供可以对其调用的所有不同的accept函数,并使Visitable接受该访问者。

为此,您可以使用类型列表来定义访问者可以接受的类型,使用类型列表作为基本visitee类,并将类型列表作为参数添加到visitee实现中。

示例草图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// assuming a typelist has typedefs first and second and a
// type 'empty' representing end of type list

template<typename Types>
class Visitor : public Visitor<Types::second> {
public:
    // visitor has a visit function for each type in Types
    virtual void visit(typename Types::first& visitable) = 0;
};

template<> class Visitor { };

template<typename Types>
class Visitable{
    public:
    // base accepts a visitor which can visit any type in Types
    virtual void accept(Visitor<Types>& visitor) = 0;
};

template<typename Derived, typename Types>
class VisitableImpl : public Visitable<Types> {
public:
    // impl calls specific visit function
    virtual void accept(Visitor<Types>& visitor) override {
        visitor.visit(static_cast<Derived&>(*this));
    }
};


我还需要一个模板化的访问者模式,并且能够创建一个不涉及变量类型或类型列表使用的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// forward declarations for our Visitable interface
class Object;
class Visitor;

// Visitable objects can accept a visitor.
class Visitable
{
public:
    virtual ~Visitable() { }
    virtual void accept_visitor(Visitor& visitor) = 0;
    virtual void accept(Object& obj);
};

// A base class, to allow downcasting
class Object
{
protected:
    virtual void _f() { }
};

// Our Visitor class, which will wrap our concrete visitor implementation
class Visitor
{
public:
    Visitor(Object* obj);

    // Base class for concrete visitors
    template<typename D, typename V>
    class OfType : public Object
    {
    public:
        void visit(V* visitable) {
            D* derived = static_cast<D*>(this);

            //"duck-typed" method; if our derived class does not have
            // this method, compilation will fail.
            derived->on_visit(visitable);
        }
    };

    template<typename D, typename V>
    void visit(V* visitable);

private:
    Object* m_obj;
};

Visitor::Visitor(Object* obj) : m_obj(obj) { }

template<typename D, typename V>
void Visitor::visit(V* visitable) {
    // check if our visitor is able to visit this instance
    OfType<D,V>* visitor = dynamic_cast<OfType<D,V>* >(m_obj);
    if (visitor) {
        visitor->visit(visitable);
    }
}

void Visitable::accept(Object& visitor) {
    Visitor wrapped(&visitor);
    accept_visitor(wrapped);
}

在定义了上述接口之后,为可访问对象的访问者创建特定的接口,然后在具体类中实现它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class This;

class ThisVisitor : public Visitor::OfType<ThisVisitor, This>
{
public:
    virtual void on_visit(This* item) = 0;
};

class This : public Visitable
{
public:
    void accept_visitor(Visitor& visitor) {
        visitor.visit<ThisVisitor>(this);
    }
};

class That;

class ThatVisitor : public Visitor::OfType<ThatVisitor, That>
{
public:
    virtual void on_visit(That* item) = 0;
};

class That : public Visitable
{
public:
    void accept_visitor(Visitor& visitor) {
        visitor.visit<ThatVisitor>(this);
    }
};

class MyVisitor : public ThisVisitor, public ThatVisitor
{
public:
    void on_visit(This* item) { printf("This!"); }
    void on_visit(That* item) { printf("That!"); }
};

int main(int argc, const char* argv[] {
    This item1;
    That item2;
    MyVisitor visitor;
    item1.accept(visitor);   //"This!"
    item2.accept(visitor);   //"That!"
}

您也可以完全跳过访客界面,让您的具体访客直接从OfType派生,但我发现,使用前者更好地扩展您的访客,因为定义了新的类(That不应该关心谁访问它,只要它是ThatVisitor类型)。