1

Why does the following code not compile under either gcc or clang:

class Foo {
public:
    void bar(int) {}
};

template< class T, typename ...Args, void(T::*Member)(Args...) >
void member_dispatch(Args&&... args, void* userdata)
{
    T* obj = static_cast<T*>(userdata);
    (obj->*Member)(std::forward<Args>(args)...);
}

int main()
{
    Foo foo;
    member_dispatch<Foo, int, &Foo::bar>(1, &foo);
    return 0;
}

See e.g. here.

This question can possibly be merged with this one, though here I get unclear compilation errors from gcc and clang (instead of VS).

2
  • 2
    Why can't you simply solve your problem with standard std::function and std::bind (or lambdas)? Or like almost all standard library functions and classes that needs callable objects, by using a single typename template argument for the function type? Commented Mar 24, 2018 at 12:15
  • This is for interfacing with C libraries that use callbacks in the form of void callback(<some params>, void* userdata) Commented Mar 24, 2018 at 12:45

2 Answers 2

3

When you explicitly specify the types, a parameter pack is greedy. &Foo::bar will be parsed as part of typename ...Args, which causes the error.

The correct way to write this is to put it in the function parameter list, instead of a non-type template parameter.

template< class T, typename ...Args>
void member_dispatch(Args&&... args, void(T::*Member)(Args...), void* userdata)
{
    T* obj = static_cast<T*>(userdata);
    (obj->*Member)(std::forward<Args>(args)...);
}

int main()
{
    Foo foo;
    member_dispatch<Foo, int>(1, &Foo::bar, &foo);
    return 0;
}

A Better Way:

It would be better to take advantage of C++'s template argument deduction. But here you doesn't put the parameter pact at the end of a function parameter list, which is a non-deduced context. So I suggest you re-order it, so that you don't need to specify the template argument:

template<class T, class K, typename ...Args>
void member_dispatch(K T::*ptr, void* userdata, Args&&... args)
{
    T* obj = static_cast<T*>(userdata);
    (obj->*ptr)(std::forward<Args>(args)...);
}

int main()
{
    Foo foo;
    member_dispatch(&Foo::bar, &foo, 1);
    return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

No wait. Empty parameter pack. You need the int. I think it'd be better to suggest a better alternative to the OP. In terms of ordering the types and parameters.
@StoryTeller I missed something. My last comment is wrong. But still non-deduced context, because pact is not the last in function parameter list
Yeah, that's what my comment is going for too. Ultimately, it'd be better to rearrange things to allow for as much deduction as possible. Utilize C++ to it's full power and all those niceties.
For my use case I can't pass the member function as a parameter, it needs to be compiled in as a non-type template parameter somehow
Anyway, I guess the answer itself appears correct, thanks. According to cppreference "In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments" and I missed that condition
0

Try using a class template instead of a function template. There has also been a std syntax proposal "template << auto x >> " for none-type template parameters. Proper implementation will simplify your library syntax.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.