Skip to content

Instantly share code, notes, and snippets.

@mpark
Last active December 5, 2016 00:55
Show Gist options
  • Save mpark/6941d3f4aba4458b1121b03dbb4861da to your computer and use it in GitHub Desktop.
Save mpark/6941d3f4aba4458b1121b03dbb4861da to your computer and use it in GitHub Desktop.
Visitor Pattern
class Cat;
class Dog;
class Animal {
public:
template <typename F>
static void visit(const F& f, const Animal& animal) {
animal.accept(Visitor<F>(f));
}
template <typename F>
static void visit(const F& f, const Animal& lhs, const Animal& rhs) {
lhs.accept(LhsVisitor<F>(f, rhs));
}
protected:
struct AnyVisitor {
virtual void operator()(const Cat&) const = 0;
virtual void operator()(const Dog&) const = 0;
};
private:
template <typename F>
struct Visitor : AnyVisitor {
explicit Visitor(const F& f) : f_(f) {}
virtual void operator()(const Cat& that) const override { f_(that); }
virtual void operator()(const Dog& that) const override { f_(that); }
const F& f_;
};
template <typename F, typename Lhs>
struct RhsVisitor : AnyVisitor {
explicit RhsVisitor(const F& f, const Lhs& lhs) : f_(f), lhs_(lhs) {}
virtual void operator()(const Cat& rhs) const override { f_(lhs_, rhs); }
virtual void operator()(const Dog& rhs) const override { f_(lhs_, rhs); }
const F& f_;
const Lhs& lhs_;
};
template <typename F>
struct LhsVisitor : AnyVisitor {
explicit LhsVisitor(const F& f, const Animal& rhs) : f_(f), rhs_(rhs) {}
virtual void operator()(const Cat& lhs) const override {
rhs_.accept(RhsVisitor<F, Cat>(f_, lhs));
}
virtual void operator()(const Dog& lhs) const override {
rhs_.accept(RhsVisitor<F, Dog>(f_, lhs));
}
private:
const F& f_;
const Animal& rhs_;
};
virtual void accept(const AnyVisitor&) const = 0;
};
class Cat : public Animal {
private:
virtual void accept(const AnyVisitor& visitor) const { visitor(*this); }
};
class Dog : public Animal {
private:
virtual void accept(const AnyVisitor& visitor) const { visitor(*this); }
};
// A new animal type simply copy/pastes the `accept` function.
#include <iostream>
#include <memory>
#include <vector>
struct Move {
void operator()(const Cat&) const {
std::cout << "Cat move" << std::endl;
}
void operator()(const Dog&) const {
std::cout << "Dog move" << std::endl;
}
};
struct Meets {
void operator()(const Cat&, const Cat&) const {
std::cout << "Cat meets Cat" << std::endl;
}
void operator()(const Cat&, const Dog&) const {
std::cout << "Cat meets Dog" << std::endl;
}
void operator()(const Dog&, const Cat&) const {
std::cout << "Dog meets Cat" << std::endl;
}
void operator()(const Dog&, const Dog&) const {
std::cout << "Dog meets Dog" << std::endl;
}
};
// Generic `Print` that takes one or two animals.
// No need to inherit from anything.
// You can even have a templated `operator()` for a "catch-all".
struct Print {
void operator()(const Dog&) const {
std::cout << "Dog" << std::endl;
}
void operator()(const Cat&, const Cat&) const {
std::cout << "Cat, Cat" << std::endl;
}
void operator()(const Dog&, const Dog&) const {
std::cout << "Dog, Dog" << std::endl;
}
template <typename... Animals>
void operator()(const Animals&...) const {
std::cout << "Don't care" << std::endl;
}
};
int main() {
// Visiting one animal with `Move`.
Animal::visit(Move{}, Cat{}); // prints: Cat move
Animal::visit(Move{}, Dog{}); // prints: Dog move
// Visiting two animals with `Meets`.
Animal::visit(Meets{}, Cat{}, Cat{}); // prints: Cat meets Cat
Animal::visit(Meets{}, Cat{}, Dog{}); // prints: Cat meets Dog
Animal::visit(Meets{}, Dog{}, Cat{}); // prints: Dog meets Cat
Animal::visit(Meets{}, Dog{}, Dog{}); // prints: Dog meets Dog
// Visiting one or two animals with `Print`.
Animal::visit(Print{}, Cat{}); // prints: Don't care
Animal::visit(Print{}, Dog{}); // prints: Dog
Animal::visit(Print{}, Cat{}, Cat{}); // prints: Cat, Cat
Animal::visit(Print{}, Cat{}, Dog{}); // prints: Don't care
Animal::visit(Print{}, Dog{}, Cat{}); // prints: Don't care
Animal::visit(Print{}, Dog{}, Dog{}); // prints: Dog, Dog
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment