I want to wrap a member function and a specific object into a function object (which I'll use as a callback later). I would like to write this wrapping function once for different member functions and objects especially because my actual lambda does some extra work before calling the wrapped method. Here are a few possible implementations:
#include <iostream>
#include <string>
#include <utility>
template <class ClassT, class... ArgsT>
auto getCallbackPtr(ClassT* obj, void(ClassT::* memfn)(ArgsT...))
{
return [obj, memfn](ArgsT&&... args) {
(obj->*memfn)(std::forward<ArgsT>(args)...);
};
}
template <auto memFn, class ClassT>
auto getCallbackTemplate(ClassT* obj)
{
return [obj](auto&&... args){
return (obj->*memFn)(std::forward<decltype(args)>(args)...);
};
}
template <auto memFn, class ClassT, class... ArgsT>
auto getCallbackRedundant(ClassT* obj)
{
return [obj](ArgsT&&... args){
return (obj->*memFn)(std::forward<ArgsT&&>(args)...);
};
}
// Example of use
class Foo {
public:
void bar(size_t& x, const std::string& s) { x=s.size(); }
};
int main() {
Foo f;
auto c1 = getCallbackPtr(&f, &Foo::bar);
size_t x1; c1(x1, "123"); std::cout << "c1:" << x1 << "\n";
auto c2 = getCallbackTemplate<&Foo::bar>(&f);
size_t x2; c2(x2, "123"); std::cout << "c2:" << x2 << "\n";
auto c3 = getCallbackRedundant<&Foo::bar, Foo, size_t&, const std::string&>(&f);
size_t x3; c3(x3, "123"); std::cout << "c3:" << x3 << "\n";
}
I would like a function that combines different aspects of the above three functions:
getCallbackPtr()
.operator()
should not be a templated function, unlike getCallbackTemplate()
.getCallbackRedundant()
.Here are my reasons for wanting the member function to be a template parameter, although I must admit these probably won't have a noticable effect in practice:
std::function
(small object optimization).Here are the problems with the getCallbackTemplate()
, which has a templated operator()
:
error C3533: a parameter cannot have a type that contains 'auto'
, in reference to template <auto memFn, class ClassT>
.)operator()
(admittedly this is just a hunch).operator()
is not capable of accepting initializer lists for arguments. This is not an issue at all for me but I mention it for the record.I think the reasons for wanting inferred template parameters are fairly clear: getCallbackRedundant()
is distractingly verbose and harder to use.
Can this be done? How?
One easy way to deduce the arguments is by using partial template specialization.
In this example I'm solving the problem by forwarding the non-type member function pointer and it's type to a custom functor, that is then returned.
Partially specialize on the type and from there the rest is straight-forward.
#include <iostream>
#include <string>
template <auto memFnPtr, class memFn>
struct getCallbackTemplate;
template <auto memFnPtr, class Ret, class ClassT, class... Args>
struct getCallbackTemplate<memFnPtr, Ret(ClassT::*)(Args...)>
{
getCallbackTemplate (ClassT* obj) : m_obj(obj) {}
Ret operator()(Args... args) {
return (m_obj->*memFnPtr)(std::forward<Args>(args)...);
}
ClassT* m_obj;
};
template <auto memFn, class ClassT>
auto getCallback(ClassT* obj) {
return getCallbackTemplate<memFn, decltype(memFn)>(obj);
}
class Foo {
public:
void bar(std::size_t& x, const std::string& s) { x=s.size(); }
};
int main() {
Foo f;
auto c1 = getCallback<&Foo::bar>(&f);
size_t x1; c1(x1, "123"); std::cout << "c1:" << x1 << "\n";
}