Search code examples
c++design-patternsvisitor-pattern

Acyclic visitor pattern. Moving the accept function to just one place


Below I've taken out the accept (const ChooseVisitor&); function out of Object and placed it in Menu<T>::Option instead so that it would only need to be called there instead of the many classes that will have the accept function. The only problem is that T may be a pointer or a reference, or whatever, and I need to turn into into a regular type without pointer or reference or const, etc... to get T::Visitor* to compile.

#include <iostream>
#include <type_traits> 

struct ChooseVisitor {
    virtual ~ChooseVisitor() = default;
};

struct Object {
    struct Visitor {
        virtual void visit (Object*) const = 0;
    };
//   void accept (const ChooseVisitor&);  // I've decided to move this into Menu instead (to avoid the repetitions in other classes).
};

struct PurchaseObjectVisitor : public ChooseVisitor, public Object::Visitor {
    virtual void visit (Object* object) const {
        std::cout << object << " purchased.\n";
    }
};

template <typename T>
struct Menu {
    struct Option {
        T item;
        template <typename> void accept (const ChooseVisitor&);
    };
    Option* option;  // Assume Menu<T> with only one option for simplicity here.
    template <typename> void choose() const;
    void insert (const T& t) {option = new Option{t};}
};

template <typename T>
template <typename Visitor>
void Menu<T>::Option::accept (const ChooseVisitor& visitor) {
//  using Type = typename std::remove_pointer<T>::type;  // How to cover all possible cases?
    const typename Type::Visitor* v = dynamic_cast<const typename Type::Visitor*> (&visitor);
    if (v) v->visit(item);
}

template <typename T>
template <typename Visitor>
void Menu<T>::choose() const {
    const Visitor visitor;
    option->template accept<Visitor>(visitor);  // template disambiguator needed.
}

int main() {
    Menu<Object*> menu;
    menu.insert (new Object);
    menu.choose<PurchaseObjectVisitor>();
}

I'm starting with

template <typename T>
struct GetVisitor {
    using type = std::is_pointer<T>::value ? typename std::remove_pointer<T>::type::Visitor : typename T::Visitor;
};

but it is not working right now, and also it needs to handle ALL the possible types. What's the most elegant way to do this? Is there a simple function that does that already?


Solution

  • You're missing ::type. It should be std::remove_pointer<T>::type

    Update: To get plain type you can nest the std::remove_ templates - std::remove_cv<typename std::remove_pointer<typename std:: remove_reference<T>::type>::type>::type