mercredi 26 août 2015

Putting virtual functions into a family

Given

class A {
    public:
        virtual int foo (int) const = 0;
        virtual void bar (char, double) const = 0;
};

class B : public A {
    virtual int foo (int) const {std::cout << "B::foo() called.\n";  return 3;}
    virtual void bar () const {std::cout << "B::bar() called.\n";}
};

class C : public B {
    virtual int foo (int) const {std::cout << "C::foo() called.\n";  return 8;}
    virtual void bar (char, double) const {std::cout << "C::bar() called.\n";}
};

I want to put foo and bar (and other virtual functions of A) into a template family of functions. Here's what I came up with so far:

#include <iostream>

enum Enum {Foo, Bar};

template <Enum> struct EnumTraits;

template <> struct EnumTraits<Foo> { using return_type = int; };
template <> struct EnumTraits<Bar> { using return_type = void; };

class A {
    template <Enum, typename...> class Execute;
public:
    virtual int foo (int) const = 0;
    virtual void bar (char, double) const = 0;
    template <Enum E, typename... Args>
    typename EnumTraits<E>::return_type execute(Args&&... args) const {
        return Execute<E, Args...>(this)(std::forward<Args>(args)...);
    }
};

template <typename... Args>
class A::Execute<Foo, Args...> {
    const A* a;
public:
    Execute (const A* a_) : a(a_) {}
    int operator()(Args&&... args) const {return a->foo(std::forward<Args>(args)...);}
};

template <typename... Args>
class A::Execute<Bar, Args...> {
    const A* a;
public:
    Execute (const A* a_) : a(a_) {}
    void operator()(Args&&... args) const {a->bar(std::forward<Args>(args)...);}
};

class B : public A {
    virtual int foo (int) const {std::cout << "B::foo() called.\n";  return 3;}
    virtual void bar () const {std::cout << "B::bar() called.\n";}
};

class C : public B {
    virtual int foo (int) const {std::cout << "C::foo() called.\n";  return 8;}
    virtual void bar (char, double) const {std::cout << "C::bar() called.\n";}
};

int main() {
    A* c = new C;

    int n = c->foo(5);  // C::foo() called.
    c->bar(3, 'c');  // C::bar() called.

    n = c->execute<Foo>(5);  // C::foo() called.
    c->execute<Bar>(3, 'c');  // C::bar() called.
}

But the partial specializations A::Execute<Foo, Args...> and A::Execute<Bar, Args...> look near-identical and should ideally be left unspecialized (especially if there are many other virtual functions than foo and bar). Written something like:

template <Enum N, typename... Args>
class A::Execute {
    const A* a;
public:
    Execute (const A* a_) : a(a_) {}
    int operator()(Args&&... args) const {return a->???(std::forward<Args>(args)...);}

};

How to fill in that ??? part? Ideally, I was hoping to use the EnumTraits class already present.

Aucun commentaire:

Enregistrer un commentaire