Acyclic Visitor Pattern with polymorphic visitors

Acyclic visitor pattern used here to count the number of elements of a certain type in a container.

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
struct A {
	struct Visitor {
		virtual ~Visitor() = default;
	};
	virtual void accept (A::Visitor&) = 0;
};

struct B : A {
	struct Visitor {
		virtual ~Visitor() = default;
		virtual void visit (B*) = 0;
	};
	virtual void accept (A::Visitor& visitor) override {
		Visitor* v = dynamic_cast<Visitor*>(&visitor);
		if (v) v->visit (this);
	}
};

struct C : B {
	struct Visitor : B::Visitor {
		virtual ~Visitor() = default;
		virtual void visit (C*) = 0;
	};
	virtual void accept (A::Visitor& visitor) override {
		Visitor* v = dynamic_cast<Visitor*>(&visitor);
		if (v) v->visit (this);
	}
};

struct D : C {
	struct Visitor : C::Visitor {
		virtual ~Visitor() = default;
		virtual void visit (D*) = 0;
	};
	virtual void accept (A::Visitor& visitor) override {
		Visitor* v = dynamic_cast<Visitor*>(&visitor);
		if (v) v->visit (this);
	}
};

template <typename T>  // to count the number of objects of type T
struct counter : virtual A::Visitor, T::Visitor {	
       int count = 0;
	virtual void visit (T*) override {
		count++;
	}
	operator int() const {return count;}
};

counter<B> countB;

int main() {
	A* objects[] = {new B, new C, new D};
	for (A* x : objects)
		x->accept (countB);
	std::cout << "count = " << countB << std::endl;
	
	std::cin.get();
}

gives the incorrect output:
count = 1 (should be 3 since C and D are derived from B).

My solution:
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
63
64
65
66
67
68
69
struct A {
	struct Visitor {
		virtual ~Visitor() = default;
	};
	virtual void accept (A::Visitor&) = 0;
};

struct B;  struct C;  struct D;

struct VisitorD {
	virtual ~VisitorD() = default;
	virtual void visit (D*) = 0;
};

struct VisitorC : VisitorD {
	virtual ~VisitorC() = default;
	virtual void visit (C*) = 0;
};

struct VisitorB : VisitorC {
	virtual ~VisitorB() = default;
	virtual void visit (B*) = 0;
};

struct B : A {
	virtual void accept (A::Visitor& visitor) override {
		VisitorB* v = dynamic_cast<VisitorB*>(&visitor);
		if (v) v->visit (this);
	}
};

struct C : B {
	struct Visitor : B::Visitor {
		virtual ~Visitor() = default;
		virtual void visit (C*) = 0;
	};
	virtual void accept (A::Visitor& visitor) override {
		VisitorC* v = dynamic_cast<VisitorC*>(&visitor);
		if (v) v->visit (this);
	}
};

struct D : C {
	struct Visitor : C::Visitor {
		virtual ~Visitor() = default;
		virtual void visit (D*) = 0;
	};
	virtual void accept (A::Visitor& visitor) override {
		VisitorD* v = dynamic_cast<VisitorD*>(&visitor);
		if (v) v->visit (this);
	}
};

struct CountB : A::Visitor, VisitorB {  // to count the number of objects of type B
	int count = 0;
	virtual void visit (B*) override {count++;}
	virtual void visit (C*) override {count++;}
	virtual void visit (D*) override {count++;}
	operator int() const {return count;}
} countB;

int main() {
	A* objects[] = {new B, new C, new D};
	for (A* x : objects)
		x->accept (countB);
	std::cout << "count = " << countB << std::endl;
	
	std::cin.get();
}

give the correct:
count = 3
but it is scrappy. The visitor classes had to be placed outside the classes they belonged to, and CountB lost its template because of that. Also, it is very awkward that I have to construct a "reverse hierarchy" for the Visitor classes (which means that this has to be painfully changed if I ever change the hierarchy for A, B, C, ...). How to improve my solution (using acyclic visitor pattern)? Forward declaring nested classes is not possible in c++.
Last edited on
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
#include <iostream>
#include <string>

struct A
{
    struct visitor { virtual ~visitor() = default ; virtual bool accept( A* ) { return false ; } };
    virtual ~A() = default ;
    virtual bool accept( visitor& ) = 0 ;
    bool accept( visitor&& v ) { return accept(v) ; }
};

template < typename BASE > struct base : BASE
{
    struct visitor { virtual ~visitor() = default ; virtual bool accept( base* ) = 0 ; };
    virtual bool accept( A::visitor& av ) override
    {
        auto dv = dynamic_cast< base::visitor* >( std::addressof(av) ) ;
        return dv ? dv->accept(this) : av.accept(this) ;
    }
};

struct B : base<A> {};
struct C : base<A> {};
struct D : base<B> {};
struct E : base<C> {};
struct F : base<D> {};

template < typename T > struct counting_visitor : A::visitor
{
    virtual bool accept( A* p ) { return dynamic_cast< T* >(p) ? ++cnt : false ; }
    operator int () const { return cnt ; }
    private: int cnt = 0 ;
};

int main()
{
    A* seq[] { new B, new C, new D, new E, new F, new C, new B, new F, new E, new B } ;

    counting_visitor<B> cvb ;
    counting_visitor<C> cvc ;
    counting_visitor<D> cvd ;
    for( A* p : seq ) { p->accept(cvb) ; p->accept(cvc) ; p->accept(cvd) ; }

    std::cout << "B: " << cvb // 6
              << "\nC: " << cvc // 4
              << "\nD: " << cvd << '\n' ; // 3
}

http://coliru.stacked-crooked.com/a/7c3bcc1b008706dd
Topic archived. No new replies allowed.