Skip to content

Latest commit

Β 

History

History
125 lines (93 loc) Β· 4.92 KB

README.md

File metadata and controls

125 lines (93 loc) Β· 4.92 KB

Actions Status Actions Status Actions Status Actions Status Actions Status codecov

Revisited

A C++17 acyclic visitor template and inheritance-aware any and any-function class. Using revisited::Visitor greatly reduces the boilerplate code required for implementing the visitor pattern in C++. It uses only compile time type information and has better performance than solutions relying on run time type information such as dynamic_cast.

Examples

See the examples directory for full examples.

Revisited Examples

Simple Visitor

#include <memory>
#include <iostream>
#include <revisited/visitor.h>

struct Base: public virtual revisited::VisitableBase { };
struct A: public Base, public revisited::Visitable<A> { };
struct B: public Base, public revisited::Visitable<B> { };

struct Visitor: public revisited::Visitor<A &,B &> {
  void visit(A &){ std::cout << "Visiting A" << std::endl; }
  void visit(B &){ std::cout << "Visiting B" << std::endl; }
};

int main() {
  std::shared_ptr<Base> a = std::make_shared<A>();
  std::shared_ptr<Base> b = std::make_shared<B>();
  
  Visitor visitor;
  a->accept(visitor); // -> Visiting A
  b->accept(visitor); // -> Visiting B
}

Derived Classes

revisited::Visitor also understands derived classes and classes with multiple visitable base classes. Virtual visitable base classes are also supported. When visiting a derived object, the first class matching the visitor is used (starting from parent classes). Multiple and virtual inheritance is fully supported.

// C is inherited from A (both can be visited)
struct C: public revisited::DerivedVisitable<C, A> { };
// D is inherited from A and B (A and B can be visited)
struct D: public revisited::JoinVisitable<A, B> { };
// E is virtually inherited from  A and B (E, A and B can be visited)
struct E: public revisited::DerivedVisitable<E, revisited::VirtualVisitable<A, B>> { };

revisited::Any Examples

Implicit casting

revisited::Any v;
v = 42;
std::cout << v.get<int>() << std::endl; // -> 42
std::cout << v.get<double>() << std::endl; // -> 42
v = "Hello Any!";
std::cout << v.get<std::string>() << std::endl; // -> Hello Any!

Reference aware casting

int x = 42;
revisited::Any a = std::reference_wrapper(x);
std::cout << a.get<double>() << std::endl; // -> 42
std::cout << &a.get<int&>() == &x << std::endl; // -> 1

Inheritance aware casting

// inheritance aware
struct MyClassBase{ int value; };
struct MyClass: public MyClassBase{ MyClass(int value):MyClassBase{value}{ } };
revisited::Any v;
v.setWithBases<MyClass, MyClassBase>(42);
std::cout << v.get<MyClassBase &>().value << std::endl; // -> 42
std::cout << v.get<MyClass &>().value << std::endl; // -> 42

revisited::AnyFunction Examples

revisited::AnyFunction f;
f = [](int x, float y){ return x + y; };
std::cout << f(40,2).get<int>() << std::endl; // -> 42

Installation and usage

With CPM, revisited::Visitor can be used in a CMake project simply by adding the following to the project's CMakeLists.txt.

CPMAddPackage(
  NAME Revisited
  GIT_REPOSITORY https://github.com/TheLartians/Visitor.git
  VERSION 2.0
)

target_link_libraries(myProject Revisited)

Alternatively, the repository can be cloned locally and included it via add_subdirectory. Installing revisited::Visitor will make it findable in CMake's find_package.

Performance

revisited::Visitor uses meta-programming to determine the inheritance hierarchy at compile-time for optimal performance. Compared to the traditional visitor pattern revisited::Visitor requires an additional virtual calls (as the type of the visitor and the visitable object are unknown). With compiler optimizations enabled, these calls should be hardly noticeable in real-world applications.

There is an benchmark suite included in the repository that compares the pure cost of the different approaches.

cmake -Hbenchmark -Bbuild/bench -DCMAKE_BUILD_TYPE=Release
cmake --build build/bench -j8
./build/bench/RevisitedBenchmark