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
Related
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;
}
Here is simplified sample of problem, featuring CRTP:
#include <type_traits>
#include <iostream>
enum ActionTypes {
eInit = 2 << 0,
eUpdate = 2 << 1,
eMultUpdate = 2 << 2
};
template <class Data,
unsigned Actions = eInit|eUpdate|eMultUpdate>
class ActionData
{
template<ActionTypes As /*???*/>
struct action {
static void exec(Data*) { std::cout << "ActionData:: /*dummy*/ exec()\n"; };
static void exec(Data*,int) { std::cout << "ActionData::/*dummy*/ exec(int)\n"; };
};
template<>
struct action < /*???*/ >
{
static void exec(Data*) { /*...*/ };
};
template<>
struct action < /*???*/ >
{
static void exec(Data*, int) { /*...*/ };
};
Data* derived() { return static_cast<Data*>(this); }
protected:
void init() { action<eInit>::exec(derived()); }
void update() { action<eUpdate>::exec(derived()); }
void update(int key) { action<eMultUpdate>::exec(derived()); }
public:
enum Keys { DEFAULT_KEY = -1 };
void call(ActionTypes a, int key = DEFAULT_KEY)
{
switch (a) {
case eInit:
init(); break;
case eUpdate:
if (key == DEFAULT_KEY)
update();
else
case eMultUpdate:
update(key);
}
}
};
class Test : public ActionData<Test, eUpdate>
{
public:
void update() { std::cout << "Test :: update()\n"; }
};
int main()
{
Test actor;
ActionTypes a = eInit;
actor.call(a, 0); // useless here but must be possible.
actor.call(eUpdate, 0);
actor.call(eUpdate);
}
Essentially not all derived classes may implement all handlers, a enum is used to declare that and a dummy version of handler must be called. The problem is that it's not possible to select any implementation but default one using enum and enable_if alone, it requires a non-type parameter, which stupefied me.
PS. Another problem is target platform is limited to C++98\C++03 or tr1 C++11 (no variadic templates). The awkward interface is a legacy of dynamic (but not used as such) polymorphic architecture using function pointers in a big C (not C++!) project. Necessity of pointers or vtable made system unstable to programmer errors leading to vtable being overwritten.
I didn't realize that I should use a partial specialization for all cases including where there is no match:
#include <type_traits>
#include <iostream>
enum ActionTypes {
eInit = 2 << 0,
eUpdate = 2 << 1,
eMultUpdate = 2 << 2
};
template <class Data,
unsigned Actions = eInit|eUpdate|eMultUpdate>
class ActionData
{
// Never gets selected
template<ActionTypes A, typename Enable = void > struct action {};
template< ActionTypes A >
struct action<A, typename std::enable_if<(A & Actions) == 0>::type >
{
static void exec(Data*) { std::cout << "ActionData:: /*dummy*/ exec()\n"; };
static void exec(Data*,int) { std::cout << "ActionData::/*dummy*/ exec(int)\n"; };
};
template< ActionTypes A >
struct action < A, typename std::enable_if<(A & Actions) == eInit>::type >
{
static void exec(Data* o) { o->Data::init(); };
};
template< ActionTypes A >
struct action < A, typename std::enable_if<(A & Actions) == eUpdate>::type >
{
static void exec(Data* o) { o->Data::update(); };
};
template< ActionTypes A >
struct action < A, typename std::enable_if<(A & Actions) == eMultUpdate>::type >
{
static void exec(Data* o, int key) { o->Data::update(key); };
};
Data* derived() { return static_cast<Data*>(this); }
protected:
void init() { action<eInit>::exec(derived()); }
void update() { action<eUpdate>::exec(derived()); }
void update(int key) { action<eMultUpdate>::exec(derived(), key); }
public:
enum Keys { DEFAULT_KEY };
void call(ActionTypes a, int key = DEFAULT_KEY)
{
switch (a) {
case eInit:
init(); break;
case eUpdate:
if (key == DEFAULT_KEY) {
update();
break;
} else {
case eMultUpdate:
update(key);
break;
};
}
}
};
class Test : public ActionData<Test, eUpdate>
{
public:
void update() { std::cout << "Test :: update()\n"; }
};
int main()
{
Test actor;
ActionTypes a = eInit;
actor.call(a, 0);
actor.call(eUpdate);
actor.call(eMultUpdate, 0);
}
I am looking for some way to mark the class I want to cast to. I am quite new here on the site so please feel free to improve tags or other things.
For example, if I have:
template<class C>
class Rotateable
{
virtual void C Rotate() = 0;
};
class Circle : public Rotateable<Circle>
{
Circle Rotate() { /*impl here*/ }
};
class Square : public Rotateable<Square>
{
Square Rotate() { /*impl here*/ }
};
If I have a list or array of Rotateables, how can I store somewhere (in Rotateable?) the information of what class to try and cast to, in a way that I can access at runtime?
You can't have a virtual whose return type changes. But you can have type identifiers on polymorphic classes that tell you which class to cast to before calling the function. Take a look at something like this as the basic idea. (This is c++14 code. Feel free to strip out any parts that don't work for you if you aren't using c++14 features.)
#include <iostream>
#include <memory>
#include <vector>
size_t generate_id()
{
static size_t id = 0;
return id++;
}
template <typename T>
size_t type_id()
{
static size_t id = generate_id();
return id;
}
class BaseRotatable
{
public:
template <typename T>
bool is()
{
return type_id<T>() == type();
}
virtual size_t type() = 0;
};
template <typename T>
class Rotatable : public BaseRotatable
{
public:
size_t type() override
{
return type_id<T>();
}
};
class Circle : public Rotatable<Circle>
{
public:
Circle Rotate()
{
return *this; // Make this do something fancier.
}
};
class Square : public Rotatable<Square>
{
public:
Square Rotate()
{
return *this; // Make this do something fancier.
}
};
template <typename T, typename... Args>
std::unique_ptr<BaseRotatable> factory(Args... args)
{
T* ptr = new T(args...);
return std::unique_ptr<BaseRotatable>{dynamic_cast<BaseRotatable*>(ptr)};
}
int main() {
// Build a vector of rotatables.
std::vector<std::unique_ptr<BaseRotatable>> rotatables;
rotatables.emplace_back(factory<Circle>());
rotatables.emplace_back(factory<Square>());
for (auto& rotatable : rotatables)
{
// You can also use a switch here.
if (rotatable->is<Circle>())
{
Circle& circle = *dynamic_cast<Circle*>(rotatable.get());
auto new_circle = circle.Rotate();
std::cout << "Type id: " << new_circle.type() << std::endl;
}
else if (rotatable->is<Square>())
{
Square& square = *dynamic_cast<Square*>(rotatable.get());
auto new_square = square.Rotate();
std::cout << "Type id: " << new_square.type() << std::endl;
}
}
return 0;
}
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);
}
This is what I would like to do using templates:
struct op1
{
virtual void Method1() = 0;
}
...
struct opN
{
virtual void MethodN() = 0;
}
struct test : op1, op2, op3, op4
{
virtual void Method1(){/*do work1*/};
virtual void Method2(){/*do work2*/};
virtual void Method3(){/*do work3*/};
virtual void Method4(){/*do work4*/};
}
I would like to have a class that simply derives from a template class that provides these method declarations while at the same time making them virtual. This is what I've managed to come up with:
#include <iostream>
template< size_t N >
struct ops : ops< N - 1 >
{
protected:
virtual void DoStuff(){ std::cout<<N<<std::endl; };
public:
template< size_t i >
void Method()
{ if( i < N ) ops<i>::DoStuff(); }
//leaving out compile time asserts for brevity
};
template<>
struct ops<0>
{
};
struct test : ops<6>
{
};
int main( int argc, char ** argv )
{
test obj;
obj.Method<3>(); //prints 3
return 0;
}
However, as you've probably guessed, I am unable to override any of the 6 methods I have inherited. I'm obviously missing something here. What is my error? No, this isn't homework. This is curiosity.
Tested with GCC 4.3. Don't even know why I spent time on this :-/
#include <iostream>
template <std::size_t N>
struct mark
{ };
template <std::size_t N>
struct op : op <N - 1>
{
virtual void do_method (const mark <N>&) = 0;
};
template <>
struct op <1>
{
virtual void do_method (const mark <1>&) = 0;
};
struct test : op <2>
{
template <std::size_t K>
void
method ()
{ do_method (mark <K> ()); }
virtual void do_method (const mark <1>&)
{ std::cout << "1\n"; }
virtual void do_method (const mark <2>&)
{ std::cout << "2\n"; }
};
int
main ()
{
test x;
x.method <1> ();
x.method <2> ();
}
I don't know how to move the "prettifier" method() template function out of test.
template< size_t N >
struct ops : ops< N - 1 >
This codes an endless loop. The recursion doesn't stop when N reaches 0. Add a specialization for the end case, immediately after the primary template:
template<>
struct ops<0> {}
Also, what does this do? Why not just call ops<i>::DoStuff() directly?
template< size_t i >
void Method()
{ if( i < N ) ops<i>::DoStuff(); }
To mimic your original desire:
#define MAKE_OPS(N) template<> struct Ops<N> : Ops<N-1> { virtual void Method##N() = 0; }
template<int N>
struct Ops;
template<>
struct Ops<0> { };
MAKE_OPS(1);
MAKE_OPS(2);
template<> struct Ops<3> : Ops<2> { virtual void Method3() { std::cout << "3" << std::endl; } };
MAKE_OPS(4);
MAKE_OPS(5);
MAKE_OPS(6);
struct Test : Ops<3> {
virtual void Method1() { std::cout << 1 << std::endl; }
virtual void Method2() { std::cout << 2 << std::endl; }
};