3
votes

I learned a bit about variadic templates and searched over the Internet for some samples and now trying to write some tricky code to call member a method of a variadic class template with one of its fields. I can't understand why it doesn't work. Please, help.

Here is sample classes:

class BarBase
{
public:
    BarBase() = default;

    virtual void call() = 0;
};

template<typename O, typename M, typename... A>
class Bar
    : public BarBase
{
public:
    Bar(O* o, M m, A&&... a)
        : BarBase()
        , m_o(o), m_m(m), m_a(std::forward<A>(a)...)
    { }

    void call() override final
    {
        callInnerWithArgsInside();
    }

private:
    void callInnerWithArgsInside()
    {
        (m_o->*m_m)(m_a); // Some errors happends here
    }

    O* m_o;
    M m_m;
    std::tuple<typename std::remove_reference<A>::type...> m_a;
};

template<typename O, typename M, typename... A>
BarBase* crateBar(O* o, M m, A&&... a)
{
    return new Bar<O, M, A...>(o, m, std::forward<A>(a)...);
}

And call from main:

struct Foo
{
    void foo(int ii, float ff, std::string ss)
    {
        std::cout << "called" << std::endl;
    }
};

int main()
{
    Foo f;
    int i = 10;
    float ff = 20.2f;
    std::string s = "Hello";

    BarBase* bar = crateBar(&f, &Foo::foo, i, ff, s);
    bar->call();
}

Errors:

main.cpp

1>d:\drafts_tests\main.cpp(203): error C2198: 'void (__thiscall Foo::* )(int,float,std::string)' : too few arguments for call

1> d:\drafts_tests\main.cpp(202) : while compiling class template member function 'void Bar::callInnerWithArgsInside(void)'

1> with

1> [

1> O=Foo

1> , M=void (__thiscall Foo::* )(int,float,std::string)

1> ]

1> d:\drafts_tests\main.cpp(197) : see reference to function template instantiation 'void Bar::callInnerWithArgsInside(void)' being compiled

1> with

1> [

1> O=Foo

1> , M=void (__thiscall Foo::* )(int,float,std::string)

1> ]

1> d:\drafts_tests\main.cpp(214) : see reference to class template instantiation 'Bar' being compiled

1> with

1> [

1> O=Foo

1> , M=void (__thiscall Foo::* )(int,float,std::string)

1> ]

1> d:\drafts_tests\main.cpp(225) : see reference to function template instantiation 'BarBase *crateBar(O *,M,int &,float &,std::string &)' being compiled

1> with

1> [

1> O=Foo

1> , M=void (__thiscall Foo::* )(int,float,std::string)

1> ]

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

1
How exactly doesn't it work?n. 1.8e9-where's-my-share m.
I think you're going to find this question a fascinating read.WhozCraig

1 Answers

2
votes

you are passing a tuple to the function, rather than the individual type arguments. The following will pass the required type args to the call:

template<std::size_t... I>
void callInnerWithArgsInside2(std::index_sequence<I...>)
{
    (m_o->*m_m)(std::get<I>(m_a)...); 
}

void callInnerWithArgsInside()
{
    return callInnerWithArgsInside2( std::make_index_sequence<sizeof...(A)>());
}

live demo


EDIT1: C++11 version

I have implemented a C++11 version, see updated live demo