aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/Visitor.h
blob: 95e60a1f6df261d84847ea9e9672af141c110197 (plain) (blame)
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#pragma once

#include <base/demangle.h>
#include <base/TypeList.h>
#include <Common/Exception.h>
#include <typeinfo>

/* Generic utils which are intended for visitor pattern implementation.
 * The original purpose is to provide possibility to get concrete template specialisation for type in list.
 *
 * Usage:
 *  1. Declare visitor interface base class for types T_1, ..., T_N:
 *      class MyVisitor : public Visitor<T_1, ..., T_N> {};
 *  2. Declare visitor implementation base class using VisitorImpl:
 *      template <typename Derived>
 *      class MyVisitorImpl : public VisitorImpl<Derived, MyVisitor> {};
 *  3. Add virtual function 'accept' to common base of T_1, ..., T_N:
 *      class T_Base
 *      {
 *          ...
 *      public:
 *          virtual void accept(MyVisitor &) { throw Exception("Accept not implemented"); }
 *      };
 *  4. Declare base class for T_1, ..., T_N implementation:
 *      template <typename Derived>
 *      class T_Impl : public Visitable<Derived, T_Base, MyVisitor> {};
 *  5. Implement 'accept' for each T_i:
 *     a) inherit from T_Impl:
 *      class T_i : public T_Impl<T_i> { ... };
 *     b) or in order to avoid ambiguity:
 *      class T_i
 *      {
 *          ...
 *      public:
 *          void accept(MyVisitor & visitor) override { visitor.visit(*this); }
 *      };
 *  6. Implement concrete visitor with visitImpl template function:
 *      class MyConcreteVisitor : public MyVisitorImpl<MyConcreteVisitor>
 *      {
 *          ...
 *      public:
 *          template <typename T>
 *          void visitImpl(T & t) { ... }
 *      };
 *  7. Now you can call concrete implementation for MyConcreteVisitor:
 *      MyConcreteVisitor visitor;
 *      T_Base * base;
 *      base->accept(visitor); /// call MyConcreteVisitor::visitImpl(T & t)
 *
 *  TODO: Add ConstVisitor with 'visit(const Type &)' function in order to implement 'accept(...) const'.
 */

namespace DB
{

namespace ErrorCodes
{
    extern const int LOGICAL_ERROR;
}

template <typename ... Types>
class Visitor;

template <>
class Visitor<>
{
public:
    using List = TypeList<>;

protected:
    ~Visitor() = default;
};

template <typename Type>
class Visitor<Type> : public Visitor<>
{
public:
    using List = TypeList<Type>;

    virtual void visit(Type &) = 0;

protected:
    ~Visitor() = default;
};

template <typename Type, typename ... Types>
class Visitor<Type, Types ...> : public Visitor<Types ...>
{
public:
    using List = TypeList<Type, Types ...>;
    using Visitor<Types ...>::visit;

    virtual void visit(Type &) = 0;

protected:
    ~Visitor() = default;
};


template <typename Derived, typename VisitorBase, typename ... Types>
class VisitorImplHelper;

template <typename Derived, typename VisitorBase>
class VisitorImplHelper<Derived, VisitorBase> : public VisitorBase
{
protected:
    ~VisitorImplHelper() = default;
};

template <typename Derived, typename VisitorBase, typename Type>
class VisitorImplHelper<Derived, VisitorBase, Type> : public VisitorBase
{
public:
    using VisitorBase::visit;
    void visit(Type & value) override { static_cast<Derived *>(this)->visitImpl(value); }

protected:
    template <typename T>
    void visitImpl(Type &)
    {
        throw Exception(ErrorCodes::LOGICAL_ERROR, "visitImpl({} &) is not implemented for class{}",
                        demangle(typeid(T).name()), demangle(typeid(Derived).name()));
    }

    ~VisitorImplHelper() = default;
};

template <typename Derived, typename VisitorBase, typename Type, typename ... Types>
class VisitorImplHelper<Derived, VisitorBase, Type, Types ...>
        : public VisitorImplHelper<Derived, VisitorBase, Types ...>
{
public:
    using VisitorImplHelper<Derived, VisitorBase, Types ...>::visit;
    void visit(Type & value) override { static_cast<Derived *>(this)->visitImpl(value); }

protected:
    template <typename T>
    void visitImpl(Type &)
    {
        throw Exception(ErrorCodes::LOGICAL_ERROR, "visitImpl({} &) is not implemented for class{}",
                        demangle(typeid(T).name()), demangle(typeid(Derived).name()));
    }

    ~VisitorImplHelper() = default;
};

template <typename Derived, typename VisitorBase>
class VisitorImpl : public
    TypeListChangeRoot<
        VisitorImplHelper,
        TypeListConcat<
            TypeList<Derived, VisitorBase>,
            typename VisitorBase::List
        >
    >
{
protected:
    ~VisitorImpl() = default;
};

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

}