Amir Kirsh Amir Kirsh - 1 month ago 10
C++ Question

Static Polymorphism for method and object selection in C++

Trying to get compile time method and object selection without base class and virtual calls.

Here is the case:

class A {
public:
void f1()const { cout << "A::f1" << endl;}
void f2()const { cout << "A::f2" << endl;}
};

class B {
public:
void f1()const { cout << "B::f1" << endl;}
void f2()const { cout << "B::f2" << endl;}
};

class Holder {
A* _a = nullptr;
B* _b = nullptr;
public:
Holder(A* a): _a(a) {}
Holder(B* b): _b(b) {}
void f1()const {
if(_a) _a->f1();
else if(_b) _b->f1();
}
void f2()const {
if(_a) _a->f2();
else if(_b) _b->f2();
}
};

void f(const Holder& h) {
h.f1();
}

int main() {
B obj;
Holder h(&obj);
f(h);
}


http://coliru.stacked-crooked.com/a/4b5acec6866cfd4e

Suppose there are very few classes like A and B but there may be a lot of functions like f1 and f2.

Holder needs to call function on the actual object that it holds, without polymorphism and without a need for inheritance / shared interface for A and B.

Looking for a nice way to do something like:

class Holder {
A* _a = nullptr;
B* _b = nullptr;
public:
Holder(A* a): _a(a) {}
Holder(B* b): _b(b) {}
// below is pseudo code!
void call<function>()const {
if(_a)
_a->function(); // function is known in compile time, sort of...
else if(_b)
_b->function();
}

void f1()const { call<f1>(); }
void f2()const { call<f2>(); }
};


Any idea?




  • macro?

  • template?

  • other trick?


Answer

You can use a variant. This reduces the storage from N to 1 pointer plus 1 int, and doesn't require changing anything except Holder:

#include <boost/variant.hpp>

struct f1visitor
{
    typedef void result_type;
    template<typename T>
    void operator()(T* const t) const { t->f1(); }
};

struct f2visitor
{
    typedef void result_type;
    template<typename T>
    void operator()(T* const t) const { t->f2(); }
};

class Holder {
    boost::variant<A*, B*> _ptr;
public:
    Holder(A* a): _ptr(a) {}
    Holder(B* b): _ptr(b) {}
    void f1()const {
        boost::apply_visitor(f1visitor(), _ptr);
    }
    void f2()const {
        boost::apply_visitor(f2visitor(), _ptr);
    }
};

With new enough C++ you may be able to use std::variant instead.