I am researching a design for a project and I have an issue to overcome. Basically I need the template constructor of visitor_interfaces_t to be called. Below is a simplified example of how the system works syntactically. The "large scale" project is located at https://github.com/amatarazzo777/ux_gui_stream and is in development by myself. The objects are organized to be easily created and also maintained. So I do like the syntax of how coordinate_t is written. However, it is not functioning as I planned. What is the proper way to inherit visitor_interfaces_t and allow the template param pack constructor to it to be called? C++17 dialect. Any other rigorous dissection is also welcomed, such as better design. Thanks in advance for help.
I did find another solution to this question. I decided to break up the process of broadcasting the interface position and resolving the member function to the implementation.
template header file (templates_inherit.h)
/**
templates_inherit.h
*/
#ifndef TEMPLATES_INHERIT_H_
#define TEMPLATES_INHERIT_H_
typedef int cairo_t;
typedef double PangoLayout;
class system_base_t {
public:
system_base_t() {
if (!visitor_dispatch_bound)
;
}
virtual ~system_base_t() {}
bool visitor_dispatch_bound = false;
};
template <typename T, typename TC, typename... Args>
class class_storage_emitter_t : public TC,
public Args...,
public system_base_t {
public:
using TC::TC;
class_storage_emitter_t() {}
class_storage_emitter_t(const class_storage_emitter_t &other) : TC(other) {}
class_storage_emitter_t(class_storage_emitter_t &&other) noexcept
: TC(other) {}
class_storage_emitter_t &operator=(const class_storage_emitter_t &other) {
TC::operator=(other);
return *this;
}
class_storage_emitter_t &operator=(class_storage_emitter_t &&other) noexcept {
TC::operator=(other);
return *this;
}
virtual ~class_storage_emitter_t() {}
};
typedef std::function<void(cairo_t *)> fn_emit_cr_t;
typedef std::function<void(PangoLayout *)> fn_emit_layout_t;
typedef std::variant<std::monostate, fn_emit_cr_t, fn_emit_layout_t>
fn_emit_overload_t;
class visitor_interface_t {
public:
fn_emit_overload_t fn = {};
std::size_t pipeline_order = {};
virtual void bind_dispatch(system_base_t *ptr) {}
};
class visitor_interfaces_base_t {
public:
visitor_interfaces_base_t() {}
virtual ~visitor_interfaces_base_t() {}
std::unordered_map<std::type_index, visitor_interface_t *>
accepted_interfaces = {};
};
template <typename... Args>
class visitor_interfaces_t : public visitor_interfaces_base_t, public Args... {
public:
visitor_interfaces_t() : Args(this)... {}
};
// abstract classes. started having problems when I added
// "interface" function (clue)
template <std::size_t ORDER> class abstract_emit_cr_t : visitor_interface_t {
public:
abstract_emit_cr_t(visitor_interfaces_base_t *ptr) {
pipeline_order = ORDER;
ptr->accepted_interfaces[std::type_index(typeid(fn_emit_cr_t))] = this;
}
void bind_dispatch(system_base_t *ptr) {
fn = fn_emit_cr_t{std::bind(&abstract_emit_cr_t::emit,
dynamic_cast<abstract_emit_cr_t *>(ptr),
std::placeholders::_1)};
}
virtual ~abstract_emit_cr_t() {}
virtual void emit(cairo_t *cr) = 0;
};
template <std::size_t ORDER>
class abstract_emit_layout_t : visitor_interface_t {
public:
abstract_emit_layout_t() {}
abstract_emit_layout_t(visitor_interfaces_base_t *ptr) {
pipeline_order = ORDER;
ptr->accepted_interfaces[std::type_index(typeid(fn_emit_layout_t))] = this;
}
void bind_dispatch(system_base_t *ptr) {
fn = fn_emit_layout_t{std::bind(&abstract_emit_layout_t::emit,
dynamic_cast<abstract_emit_layout_t *>(ptr),
std::placeholders::_1)};
}
virtual ~abstract_emit_layout_t() {}
virtual void emit(PangoLayout *layout) = 0;
};
const int order_render_option = 3;
class coordinate_storage_t {
public:
coordinate_storage_t() {}
coordinate_storage_t(double _x, double _y, double _w, double _h)
: x(_x), y(_y), w(_w), h(_h) {}
coordinate_storage_t(double _x, double _y) : x(_x), y(_y) {}
virtual ~coordinate_storage_t() {}
double x = {};
double y = {};
double w = {};
double h = {};
};
/**
* classes are named and manufactured like so.
* the parameterized nature of the syntax yields
* a very maintainable source base.
*/
using coordinate_t = class coordinate_t
: public class_storage_emitter_t<
coordinate_t, coordinate_storage_t,
visitor_interfaces_t<abstract_emit_cr_t<order_render_option>,
abstract_emit_layout_t<order_render_option>>> {
public:
using class_storage_emitter_t::class_storage_emitter_t;
coordinate_t() {}
void emit(cairo_t *cr) { std::cout << "emit cr" << std::endl; }
void emit(PangoLayout *layout) { std::cout << "emit layout" << std::endl; }
};
#endif /* TEMPLATES_INHERIT_H_ */
template_inherit.cpp
#include <functional>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <iostream>
#include <any>
#include <variant>
#include "templates_inherit.h"
using namespace std;
int main() {
coordinate_t pos = coordinate_t{10, 10, 500, 500};
pos.init_dispatch();
cout << pos.x << " " << pos.y << " " << pos.w << " " << pos.h << endl;
cairo_t dummy_cr = 1;
PangoLayout dummy_layout = 2;
for (auto n : pos.accepted_interfaces) {
std::visit(
[&](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, fn_emit_cr_t>)
arg(&dummy_cr);
else if constexpr (std::is_same_v<T, fn_emit_layout_t>)
arg(&dummy_layout);
},
n.second->fn);
}
return 0;
}
Related
I read this article about C++ factory class with a self registering capability of the concrete classes. Really like it, expecially the demangled name solution used as a key for the registered classes.
There is one main issue I would like to solve: how can we modify the factory to be able to support concrete classes with different constructor parameters?
// Dog and Cat both derive from Animal, but have different constructor parameters
class Dog : public Animal::Registrar<Dog> {
public:
Dog(int param1, std::string param2) : m_x(param1) {}
void makeNoise() { std::cerr << "Dog: " << m_x << "\n"; }
private:
int m_x;
};
class Cat : public Animal::Registrar<Cat> {
public:
Cat(bool param1) : m_flag(param1) {}
void makeNoise() { std::cerr << "Cat: " << m_x << "\n"; }
private:
bool m_flag;
};
I was thinking that maybe the parameter pack of template <class Base, class... Args> class Factory should be moved to the template <class T> struct Registrar, but I can't found a proper solution.
Full original code below
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <cstdlib>
#include <cxxabi.h>
std::string demangle(const char *name) {
int status = -4; // some arbitrary value to eliminate the compiler warning
std::unique_ptr<char, void (*)(void *)> res{
abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
return (status == 0) ? res.get() : name;
}
template <class Base, class... Args> class Factory {
public:
template <class ... T>
static std::unique_ptr<Base> make(const std::string &s, T&&... args) {
return data().at(s)(std::forward<T>(args)...);
}
template <class T> struct Registrar : Base {
friend T;
static bool registerT() {
const auto name = demangle(typeid(T).name());
Factory::data()[name] = [](Args... args) -> std::unique_ptr<Base> {
return std::make_unique<T>(std::forward<Args>(args)...);
};
return true;
}
static bool registered;
private:
Registrar() : Base(Key{}) { (void)registered; }
};
friend Base;
private:
class Key {
Key(){};
template <class T> friend struct Registrar;
};
using FuncType = std::unique_ptr<Base> (*)(Args...);
Factory() = default;
static auto &data() {
static std::unordered_map<std::string, FuncType> s;
return s;
}
};
template <class Base, class... Args>
template <class T>
bool Factory<Base, Args...>::Registrar<T>::registered =
Factory<Base, Args...>::Registrar<T>::registerT();
struct Animal : Factory<Animal, int> {
Animal(Key) {}
virtual void makeNoise() = 0;
};
class Dog : public Animal::Registrar<Dog> {
public:
Dog(int x) : m_x(x) {}
void makeNoise() { std::cerr << "Dog: " << m_x << "\n"; }
private:
int m_x;
};
class Cat : public Animal::Registrar<Cat> {
public:
Cat(int x) : m_x(x) {}
void makeNoise() { std::cerr << "Cat: " << m_x << "\n"; }
private:
int m_x;
};
// Won't compile because of the private CRTP constructor
// class Spider : public Animal::Registrar<Cat> {
// public:
// Spider(int x) : m_x(x) {}
// void makeNoise() { std::cerr << "Spider: " << m_x << "\n"; }
// private:
// int m_x;
// };
// Won't compile because of the pass key idiom
// class Zob : public Animal {
// public:
// Zob(int x) : Animal({}), m_x(x) {}
// void makeNoise() { std::cerr << "Zob: " << m_x << "\n"; }
// std::unique_ptr<Animal> clone() const { return
// std::make_unique<Zob>(*this); }
// private:
// int m_x;
// };
// An example that shows that rvalues are handled correctly, and
// that this all works with move only types
struct Creature : Factory<Creature, std::unique_ptr<int>> {
Creature(Key) {}
virtual void makeNoise() = 0;
};
class Ghost : public Creature::Registrar<Ghost> {
public:
Ghost(std::unique_ptr<int>&& x) : m_x(*x) {}
void makeNoise() { std::cerr << "Ghost: " << m_x << "\n"; }
private:
int m_x;
};
int main() {
auto x = Animal::make("Dog", 3);
auto y = Animal::make("Cat", 2);
x->makeNoise();
y->makeNoise();
auto z = Creature::make("Ghost", std::make_unique<int>(4));
z->makeNoise();
return 0;
}
Say I wish to define a member variable in a parent class and set its value in an inherited class. Perhaps these identify functionality available in the class or the nature of the child class. For example:
class A
{
public:
inline int getX() { return x; }
protected:
const int x = 0;
};
class B : public A
{
protected:
const int x = 10;
};
class C : public A
{
protected:
const int x = 50;
};
It should go without saying that scope issues will prevent the above from working properly. However, is there a way to make this work as intended?
Since the variable is meant to identify the nature of the inherited classes, I would prefer if it were const - this problem would not arise if it were not const and merely redefined in the constructor, so far as I can tell.
While fiddling with the compiler trying to make sure my example code made sense, I actually came across the fact that the way I was attempting to define the constants was C++11-specific. That led me to look into the ways it was done before, and I found this question, which shed some light on the matter indirectly.
Defining a variable in this way should be done by having the base class take an argument in its constructor, in the form of:
class A
{
public:
A( const int& type ) : x(type) {}
inline int getX() { return x; }
protected:
const int x;
};
class B : public A
{
public:
B() : A(10) {}
};
class C : public A
{
public:
C() : A(50) {}
};
This will work as intended and allow the constant x to be redefined by inherited classes.
To demonstrate the point I made in my comment, here is an example of what I think you're trying to do (deduced from comments).
I have provided both duck-typed and polymorphic solutions in the same program with a timed run through each.
I use 10 million samples of each to eliminate memory cache noise.
You will notice that the run time of the polymorphic solution is significantly less than that of the duck-typed solution.
#ifdef _WIN32
#include <Windows.h>
double get_cpu_time(){
FILETIME a,b,c,d;
if (GetProcessTimes(GetCurrentProcess(),&a,&b,&c,&d) != 0){
// Returns total user time.
// Can be tweaked to include kernel times as well.
return
(double)(d.dwLowDateTime |
((unsigned long long)d.dwHighDateTime << 32)) * 0.0000001;
}else{
// Handle error
return 0;
}
}
#else
#include <sys/time.h>
inline double get_cpu_time() noexcept {
return (double)clock() / CLOCKS_PER_SEC;
}
#endif
#include <iostream>
#include <vector>
#include <memory>
struct A
{
A(bool copy_) : copy{copy_} {}
virtual ~A() = default;
const bool copy = false;
};
struct RealA : public A
{
RealA() : A { false } {}
};
struct CopyA : public A
{
CopyA() : A { true } {}
};
// A Thing holder will hold any object which has an interface supports do_something_to(T& thing)
struct AHolder {
template<class Thing>
AHolder(std::unique_ptr<Thing> ptr)
: _ptr { std::move(ptr) }
{
}
template<class Thing, class...Args>
static AHolder construct(Args&&...args)
{
return AHolder { std::make_unique<model<Thing>>(std::forward<Args>(args)...) };
}
void do_something() const {
_ptr->do_something();
}
private:
struct concept {
virtual ~concept() = default;
virtual void do_something() = 0;
};
template<class Thing> struct model : concept {
template<class...Args>
model(Args&&...args) : _thing { std::forward<Args>(args)... } {}
private:
void do_something() override {
do_something_to(_thing);
}
Thing _thing;
};
std::unique_ptr<concept> _ptr;
};
using namespace std;
size_t copies_processed = 0;
size_t reals_processed = 0;
void do_something_to(const CopyA&)
{
// simulate work
++copies_processed;
}
void do_something_to(const RealA&)
{
// simulate work
++reals_processed;
}
int main(int argc, char **argv) {
std::vector<std::unique_ptr<A>> duck_typing;
std::vector<AHolder> polymorphic;
constexpr size_t samples = 10000000;
for (size_t i = 0 ; i < samples ; ++i) {
if (i % 2) {
duck_typing.push_back(make_unique<RealA>());
polymorphic.emplace_back(AHolder::construct<RealA>());
}
else {
duck_typing.push_back(make_unique<CopyA>());
polymorphic.emplace_back(AHolder::construct<CopyA>());
}
}
auto duck_start = get_cpu_time();
// nasty duck-typing solution
for (const auto& ptr : duck_typing) {
if (ptr->copy) {
do_something_to(*(static_cast<CopyA*>(ptr.get())));
}
else {
do_something_to(*(static_cast<RealA*>(ptr.get())));
}
}
auto duck_stop = get_cpu_time();
auto poly_start = get_cpu_time();
for (const auto& a_like : polymorphic) {
a_like.do_something();
}
auto poly_stop = get_cpu_time();
cout << "duck typing : " << duck_stop - duck_start << endl;
cout << "polymorphic : " << poly_stop - poly_start << endl;
cout << "copies processed : " << copies_processed << endl;
cout << "reals processed : " << reals_processed << endl;
return 0;
}
sample output :
duck typing : 0.162985
polymorphic : 0.137561
copies processed : 10000000
reals processed : 10000000
I have written a small piece of code where I am able to call setter and getter functions packed within a functoid using mem_fun templates.
I now would like to use this approach on top of a class hierarchy where every class might have getter and setter which can be registered as pair within a vector or array to be able to call the getter and setter if needed. GUIObject and GUICompositeObject are example classes out of the described class hierarchy.
The bound_mem_fun_t for the objects have unfortunately different types and thats the reason I don't know how to integrate them into an array/vector of pointers to the functors.
In c++11 I would use std::function. Is there a way to emulate this in c++98?
Because our compiler support only c++98 I cannot use the new features of c++11 or c++14. Also boost is not allowed.
#include <functional>
class GUIObject
{
int m_Alpha;
public:
void SetAlpha(int a) { m_Alpha = a;};
int GetAlpha() {return m_Alpha;};
};
class GUICompositeObject: public GUIObject
{
int m_NumOfChilds;
public:
void SetNumOfChilds(int NumOfChilds) { m_NumOfChilds = NumOfChilds;};
int GetNumOfChilds() {return m_NumOfChilds;};
};
template<typename T>
struct bound_mem_fun_t
{
bound_mem_fun_t(std::mem_fun_t<int, T> GetFunc, std::mem_fun1_t<void, T, int> SetFunc, T* o) :
m_GetFunc(GetFunc), m_SetFunc(SetFunc), obj(o) { } ;
int operator()() { return m_GetFunc(obj); } ;
void operator()(int i) { m_SetFunc(obj, i); } ;
std::mem_fun_t<int, T> m_GetFunc;
std::mem_fun1_t<void, T, int> m_SetFunc;
T* obj;
};
int main()
{
GUIObject kGUIObject;
GUICompositeObject kCompObj;
bound_mem_fun_t<GUIObject> GUIObjectFunc(std::mem_fun(&GUIObject::GetAlpha), std::mem_fun(&GUIObject::SetAlpha), &kGUIObject);
GUIObjectFunc(17);
int ii = GUIObjectFunc();
bound_mem_fun_t<GUICompositeObject> GUICompObjectFunc(std::mem_fun(&GUICompositeObject::GetNumOfChilds), std::mem_fun(&GUICompositeObject::SetNumOfChilds), &kCompObj);
GUICompObjectFunc(17);
int iChilds = GUICompObjectFunc();
return 0;
}
Here is the complete solution after #filmors answer:
#include <functional>
#include <vector>
#include <iostream>
class GUIObject
{
int m_Alpha;
public:
void SetAlpha(int a) { m_Alpha = a;};
int GetAlpha() {return m_Alpha;};
};
class GUICompositeObject: public GUIObject
{
int m_NumOfChilds;
public:
void SetNumOfChilds(int NumOfChilds) { m_NumOfChilds = NumOfChilds;};
int GetNumOfChilds() {return m_NumOfChilds;};
};
struct bound_mem_fun_base
{
virtual int operator()() =0;
virtual void operator()(int) =0;
};
template<typename T>
struct bound_mem_fun_t : public bound_mem_fun_base
{
bound_mem_fun_t(std::mem_fun_t<int, T> GetFunc, std::mem_fun1_t<void, T, int> SetFunc, T* o) :
m_GetFunc(GetFunc), m_SetFunc(SetFunc), obj(o) { } ;
virtual int operator()() { return m_GetFunc(obj); } ;
virtual void operator()(int i) { m_SetFunc(obj, i); } ;
std::mem_fun_t<int, T> m_GetFunc;
std::mem_fun1_t<void, T, int> m_SetFunc;
T* obj;
};
template<typename T> bound_mem_fun_t<T>* make_setter(std::mem_fun_t<int, T> GetFunc, std::mem_fun1_t<void, T, int> SetFunc, T* o)
{
return new bound_mem_fun_t<T> (GetFunc, SetFunc, o);
}
int main()
{
GUIObject kGUIObject;
GUICompositeObject kCompObj;
std::vector<bound_mem_fun_base*> kBoundVector;
kBoundVector.push_back(new bound_mem_fun_t<GUIObject> (std::mem_fun(&GUIObject::GetAlpha), std::mem_fun(&GUIObject::SetAlpha), &kGUIObject));
kBoundVector.push_back(new bound_mem_fun_t<GUICompositeObject> (std::mem_fun(&GUICompositeObject::GetNumOfChilds), std::mem_fun(&GUICompositeObject::SetNumOfChilds), &kCompObj));
kBoundVector.push_back(make_setter<GUIObject> (std::mem_fun(&GUIObject::GetAlpha), std::mem_fun(&GUIObject::SetAlpha), &kGUIObject));
kBoundVector.push_back(make_setter<GUICompositeObject> (std::mem_fun(&GUICompositeObject::GetNumOfChilds), std::mem_fun(&GUICompositeObject::SetNumOfChilds), &kCompObj));
for (int i = 0; i < 4 ; i++)
{
(*kBoundVector[i])(i*10);
int res = (*kBoundVector[i])();
std::cout << "Getter result " << res << "\n";
}
return 0;
}
Unfortunately the make_setter function does not really shorten the creation of the functor. Any ideas will be welcome.
Just give your bound_mem_fun_t<T> a common base class and use dynamic dispatch to solve your problem:
struct bound_mem_fun_base {
virtual int operator()() = 0;
virtual void operator()(int) = 0;
};
template <typename T>
struct bound_mem_fun_t : bound_mem_fun_t ...
Then you can keep pointers to bound_mem_fun_base in your vector and call the elements as (*v[0])().
Also, TR1 does contain std::tr1::function, is that available?
First a remark on std::function from c++11: That will not solve your problem, because you need an already bounded function pointer. This pointer must be bound to your object. I believe what you need is an own implementation to std::bind.
I started only a very! small Binder class which is hopefully a starting point for your needs. If you need to have template parameter lists in older c++ versions, take a look for loki. http://loki-lib.sourceforge.net/
As a hint I can give you a short example of what i did:
class A
{
private:
int val;
public:
A(int i): val(i) {}
void Do(int i) { std::cout << "A " << val<< " " << i << std::endl; }
};
class B
{
private:
int val;
public:
B(int i): val(i){}
void Go(int i) { std::cout << "B " << val << " " << i << std::endl; }
};
class Base
{
public:
virtual void operator()(int i)=0;
};
template <typename T>
class Binder: public Base
{
void (T::*fnct)(int);
T* obj;
public:
Binder( void(T::*_fnct)(int), T*_obj):fnct(_fnct),obj(_obj){}
void operator()(int i)
{
(obj->*fnct)(i);
}
};
int main()
{
A a(100);
B b(200);
// c++11 usage for this example
//std::function<void(int)> af= std::bind( &A::Do, &a, std::placeholders::_1);
//af(1);
// hand crafted solution
Base* actions[2];
actions[0]= new Binder<A>( &A::Do, &a);
actions[1]= new Binder<B>( &B::Go, &b);
actions[0]->operator()(55);
actions[1]->operator()(77);
}
I have learned this code like inheritance by using template technique on C++. This code works.
#include <iostream>
using namespace std;
template < typename T >
class Base {
public:
explicit Base(const T& policy = T()) : m_policy(policy) {}
void doSomething()
{
m_policy.doA();
m_policy.doB();
}
private:
T m_policy;
};
class Implemented {
public:
void doA() { cout << "A"; };
void doB() { cout << "B"; };
};
int main() {
Base<Implemented> x;
x.doSomething();
return 0;
}
However, is it possible to add arguments with new typename S in doA and doB? For example, this code doesn't work by type/value mismatch errors.
#include <iostream>
using namespace std;
template < typename T, typename S >
class Base {
public:
explicit Base(const T& policy = T()) : m_policy(policy) {}
void doSomething()
{
m_policy.doA(m_s);
m_policy.doB(m_s);
}
private:
T m_policy;
S m_s;
};
template < typename S >
class Implemented {
public:
void doA(S& s) { cout << "A" << s; };
void doB(S& s) { cout << "B" << s; };
};
int main() {
Base<Implemented, int> x;
x.doSomething();
return 0;
}
I guess I must let both class Base and Implemented know about an actual type of S at main(). How can I fix this issue? Thank you for your help in advance.
In this line:
Base<Implemented, int> x;
Implemented is no longer a type, now you made it a template. But Base still expects a type - so give it one:
Base<Implemented<int>, int> x;
When Implemented was a class, you used a template parameter T. Now that Implmented is a template class, you need to use a so called template template parameter, like so:
#include <iostream>
using namespace std;
template < template <class TS> class T, typename S >
class Base {
public:
explicit Base(const T<S>& policy = T<S>()) : m_policy(policy) {}
void doSomething()
{
m_policy.doA(m_s);
m_policy.doB(m_s);
}
private:
T<S> m_policy;
S m_s;
};
template < typename S >
class Implemented {
public:
void doA(S& s) { cout << "A" << s; };
void doB(S& s) { cout << "B" << s; };
};
int main() {
Base<Implemented, int> x;
x.doSomething();
return 0;
}
I found a very interesting article on
Defining Visitors Inline in Modern C++
The solution proposed is quite complicated though.
I wonder if there is a simpler way of addressing this scenario?
code and example added below to avoid needing to follow link.
Taking the example from the paper, given the following classes:
struct Triangle;
struct Square;
struct PolygonVisitor
{
virtual ~PolygonVisitor() {}
virtual void visit(Triangle& tr) = 0;
virtual void visit(Square& sq) = 0;
};
struct Polygon
{
virtual void accept(PolygonVisitor& v) = 0;
};
struct Triangle : Polygon
{
void accept(PolygonVisitor& v) override
{
v.Visit(*this);
}
};
struct Square : Polygon
{
void accept(PolygonVisitor& v) override
{
v.Visit(*this);
}
};
An inline visitor is constructed and used to work out how many sides a
shape has:
int CountSides(Polygon& p)
{
int sides = 0;
auto v = begin_visitor<PolygonVisitor>()
.on<Triangle>([&sides](Triangle& tr)
{
sides = 3;
})
.on<Square>([&sides](Square& sq)
{
sides = 4;
})
.end_visitor();
p.Accept(v);
return sides;
}
The inline visitor is defined as follows (code taken from
https://github.com/jbcoe/inline_visitor):
template <typename T, typename F, typename BaseInnerVisitor, typename ArgsT>
class ComposeVisitor
{
public:
class InnerVisitor : public BaseInnerVisitor
{
public:
using BaseInnerVisitor::Visit;
typedef typename BaseInnerVisitor::VisitorInterface VisitorInterface;
InnerVisitor(ArgsT&& args)
: BaseInnerVisitor(std::move(args.second)), m_f(std::move(args.first))
{
}
void Visit(T& t) final
{
VisitImpl(t);
}
private:
template <typename F_ = F>
typename std::enable_if<
std::is_assignable<std::function<void(T&)>, F_>::value>::type
VisitImpl(T& t)
{
m_f(t);
}
template <typename F_ = F>
typename std::enable_if<std::is_assignable<
std::function<void(T&, VisitorInterface&)>, F_>::value>::type
VisitImpl(T& t)
{
m_f(t, *this);
}
F m_f;
};
ComposeVisitor(ArgsT&& args) : m_args(std::move(args))
{
}
template <typename Tadd, typename Fadd>
ComposeVisitor<Tadd, Fadd, InnerVisitor, std::pair<Fadd, ArgsT>>
on(Fadd&& f) &&
{
return ComposeVisitor<Tadd, Fadd, InnerVisitor, std::pair<Fadd, ArgsT>>(
std::make_pair(std::move(f), std::move(m_args)));
}
template <typename InnerVisitor_ = InnerVisitor>
typename std::enable_if<!std::is_abstract<InnerVisitor_>::value,
InnerVisitor>::type
end_visitor() &&
{
return InnerVisitor(std::move(m_args));
}
ArgsT m_args;
};
template <typename TVisitorBase>
class EmptyVisitor
{
public:
class InnerVisitor : public TVisitorBase
{
public:
using TVisitorBase::Visit;
typedef TVisitorBase VisitorInterface;
InnerVisitor(std::nullptr_t)
{
}
};
template <typename Tadd, typename Fadd>
ComposeVisitor<Tadd, Fadd, InnerVisitor, std::pair<Fadd, std::nullptr_t>>
on(Fadd&& f) &&
{
return ComposeVisitor<Tadd, Fadd, InnerVisitor,
std::pair<Fadd, std::nullptr_t>>(
std::make_pair(std::move(f), nullptr));
}
};
template <typename TVisitorBase>
EmptyVisitor<TVisitorBase> begin_visitor()
{
return EmptyVisitor<TVisitorBase>();
}
One possible way of tackling this problem is inheriting from the abstract visitor ( PolygonVisitor in the example) a new class (InlineVisitor) that takes in its constructor a std::function for each abstract method it has to implement.
Each abstract method is implemented it in term of the std::function stored
#include <functional>
#include <iostream>
struct Triangle;
struct Square;
struct PolygonVisitor
{
virtual ~PolygonVisitor() {}
virtual void visit(Triangle& tr) = 0;
virtual void visit(Square& sq) = 0;
};
struct Polygon {
virtual void accept(PolygonVisitor& v) = 0;
};
struct Triangle : Polygon
{
void accept(PolygonVisitor& v) override { v.visit(*this); }
};
struct Square : Polygon
{
void accept(PolygonVisitor& v) override { v.visit(*this); }
};
class InlineVisitor : public PolygonVisitor
{
public:
virtual void visit(Triangle& value) { triangleFx_(value); }
virtual void visit(Square& value) { squareFx_(value); }
std::function<void(Triangle&)> triangleFx_;
std::function<void(Square&)> squareFx_;
InlineVisitor(const std::function<void(Triangle&)> triangleFx,
const std::function<void(Square&)> squareFx)
: triangleFx_(triangleFx)
, squareFx_(squareFx) {}
};
int countSides(Polygon& p)
{
int sides = 0;
InlineVisitor countSidesVisitor([&sides](Triangle& tr) { sides = 3; },
[&sides](Square& sq) { sides = 4; });
p.accept(countSidesVisitor);
return sides;
}
int main(int argc, char *argv[])
{
Triangle t;
Square s;
std::cout << "sides of Triangle: " << countSides(t) << std::endl
<< "sides of Square: " << countSides(s) << std::endl;
return 0;
};
The original implementation is more general while this retains the basic idea but is a simpler