user2018182
user2018182

Reputation: 53

Replace macro with a function / template

I would like to replace a macro with a function / template:

#define FOO( f ) \
    int result = m_Member.f; \
    switch ( result ) \
    { \
    case 1: \
        m_pMember1->f; \
        break;
    case 2:
        m_pMember2->f; \
        break;
    }

class foo
{
public:
    void foo1(int x, int y)
    {
        FOO( foo1(x, y) )
    }
    void foo2(int x, double y, void *z)
    {
        FOO( foo2(x, y) )
    }
protected:
    fooA m_Member;
    fooB m_Member1;
    fooC m_Member2;
};

So my problem is that function name is passed to a macro together with arguments. I can rewrite call to something like FOO( foo1, x, y ) and then deal with variable argument list. So macro will define just a single function call. But maybe there's a more elegant solution.

Upvotes: 5

Views: 3027

Answers (3)

Jarod42
Jarod42

Reputation: 217850

Following may help: (http://ideone.com/5QIBZx)

#define Return(ret) decltype ret { return ret; }

struct foo1Caller
{
    template <typename T, typename... Args>
    auto operator () (T& t, Args... args) -> Return((t.foo1(args...)))
};

struct foo2Caller
{
    template <typename T, typename... Args>
    auto operator () (T& t, Args... args) -> Return((t.foo2(args...)))
};


class foo
{
public:
    void foo1(int x, int y)
    {
        dispatch(foo1Caller(), x, y);
    }
    void foo2(int x, double y, void *z)
    {
        dispatch(foo2Caller(), x, y, z);
    }

private:
    template <typename T, typename... Args>
    void dispatch(T caller, Args... args)
    {
        switch (caller(m_Member, args...)) {
            case 1: caller(m_Member1, args...); return;
            case 2: caller(m_Member2, args...); return;
        }
    }

    fooA m_Member;
    fooB m_Member1;
    fooC m_Member2;
};

Upvotes: 0

Matthieu M.
Matthieu M.

Reputation: 300049

As is, this is not possible.

The problem is that a template is, by nature, type-safe. Your example, however, is not. There is nothing inherent to the name foo2 that constrains the set of types on which it may be applied!

To illustrate the issue, consider a typical member function:

struct Foo { int doit(double x, double y); };

Its type is int (Foo::*)(double,double).

Note how the name of the class the method belongs to appear in the type itself.

Thus, you cannot invoke doit on two unrelated classes... so I am afraid that you'll get some repetition:

class foo {
public:
    void foo1(int x, int y)
    {
        dispatch(&fooA::foo1, &fooB::foo1, &fooC::foo1, x, y);
    }

    void foo2(int x, double y, void *z)
    {
        dispatch(&fooA::foo2, &fooB::foo2, &fooC::foo2, x, y, z);
    }

private:
    template <typename... Args>
    void dispatch(int (fooA::* f)(Args...),
                  void (fooB::* g)(Args...),
                  void (fooC::*h)(Args...),
                  Args... args)
    {
        switch((m_Member.*f)(args...)) {
        case 1: (m_Member1.*g)(args...); return;
        case 2: (m_Member2.*h)(args...); return;
        }
    }

    fooA m_Member;
    fooB m_Member1;
    fooC m_Member2;
};

And yes, it works.

Upvotes: 3

Simple
Simple

Reputation: 14400

If you can add a base class to fooB and fooC (called foo_base here) with some virtual functions then you can do this with the following code:

class foo
{
private:
    template<typename... Ts>
    void another_foo(int (fooA::*f)(Ts...), void (foo_base::*g)(Ts...), Ts const... vals)
    {
        switch ((m_Member.*f)(vals...)) {
        case 1:
            (m_pMember1->*g)(vals...);
            break;

        case 2:
            (m_pMember2->*g)(vals...);
            break;
        }
    }

public:
    void foo1(int x, int y)
    {
        another_foo(&fooA::foo1, &foo_base::foo1, x, y);
    }

    void foo2(int x, double y, void *z)
    {
        another_foo(&fooA::foo2, &foo_base::foo2, x, y);
    }

protected:
    fooA m_Member;
    fooB m_Member1;
    fooC m_Member2;
};

The virtual functions will likely get devirtualised because the static type of m_pMember1 and m_pMember2 is statically known.

Upvotes: 0

Related Questions