Skip to content

Instantly share code, notes, and snippets.

@nicuveo
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicuveo/3a4927116f033813c10e to your computer and use it in GitHub Desktop.
Save nicuveo/3a4927116f033813c10e to your computer and use it in GitHub Desktop.
Extending library visitor for user-defined types with macros (example)

While the previous gist documented my attempt at finding a working solution, this one provides a small working example.

Here, the library code declares classes A and B both inheriting form a base class Base. A Visitor is introduced, and both A and B have a apply method that calls the appropriate visitor method.

The client introduces a new class C that also inherits from base, and which is able to use a Visitor-derived class on any Base* pointer, be it a A, a B or a C under the hood.

// includes
#include <iostream>
// custom user type predeclaration
class DerivedC;
#define USER_TYPES (DerivedC)
#include "library.hh"
#include "library_static.hh"
// custom user type
class DerivedC : public Base
{
public:
void apply(const Visitor& v) const;
};
void DerivedC::apply(const Visitor& v) const
{
v(*this);
}
// custom visitor
class PrintVisitor : public Visitor
{
public:
void operator()(const DerivedA&) const;
void operator()(const DerivedB&) const;
void operator()(const DerivedC&) const;
};
void PrintVisitor::operator()(const DerivedA&) const { std::cout << "A"; }
void PrintVisitor::operator()(const DerivedB&) const { std::cout << "B"; }
void PrintVisitor::operator()(const DerivedC&) const { std::cout << "C"; }
// main
int main()
{
PrintVisitor v;
DerivedA a;
DerivedB b;
DerivedC c;
Base const* pa = &a;
Base const* pb = &b;
Base const* pc = &c;
pa->apply(v);
pb->apply(v);
pc->apply(v);
std::cout << std::endl;
}
// includes
#include "library.hh"
// destructors (and thus vtables)
Base::~Base()
{
}
DerivedA::~DerivedA()
{
}
DerivedB::~DerivedB()
{
}
#pragma once
// predeclarations
class Visitor;
// class hierarchy
class Base
{
public:
virtual ~Base();
virtual void apply(const Visitor& v) const = 0;
};
class DerivedA : public Base
{
public:
virtual ~DerivedA();
virtual void apply(const Visitor& v) const;
};
class DerivedB : public Base
{
public:
virtual ~DerivedB();
virtual void apply(const Visitor& v) const;
};
// must be included once in the client
// in a source file, not a header
// includes
#include <boost/preprocessor.hpp>
#include "library.hh"
// boost_pp magic
#define USER_TYPE_DECL(R, _, TYPE) \
virtual void operator() (TYPE const&) const = 0;
// visitor
class Visitor
{
public:
virtual ~Visitor();
#ifdef USER_TYPES
BOOST_PP_SEQ_FOR_EACH(USER_TYPE_DECL, _, USER_TYPES)
#endif
virtual void operator() (DerivedA const&) const = 0;
virtual void operator() (DerivedB const&) const = 0;
};
Visitor::~Visitor()
{
}
// apply implementation
void DerivedA::apply(const Visitor& v) const
{
v(*this);
};
void DerivedB::apply(const Visitor& v) const
{
v(*this);
};
CC = clang++ -Wweak-vtables -Wall -Wextra -Werror
client: library.a client.o
$(CC) client.o -L. -lrary -o $@
client.o: client.cc library.hh Makefile
$(CC) -c $< -o $@
library.o: library.cc library.hh Makefile
$(CC) -c $< -o $@
library.a: library.o
ar -scr $@ $<
clean:
rm -fv client library.a library.o client.o
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment