(A quick note on terminology: I use 'generalized functor' to refer to a functor that can be initialized from any callable entity, similar to the definition from Alexandrescu's Modern C++ Design. I use 'generic functor' to refer to functors in the general C++ sense, meaning anything which implements operator()
)
I'm working on a factory for generalized functors in C++11. The functor's interface looks like this:
// C-style function pointers, functors
template<typename call_type, typename res_type, typename... arg_types>
struct functor {
call_type c;
functor(call_type c) : c(c) {}
res_type operator()(arg_types... args) { return c(args...); }
};
// Member function pointers
template<typename T, typename res_type, typename... arg_types>
struct functor<res_type(T::*)(arg_types...), res_type, arg_types...> {
using call_type = res_type(T::*)(arg_types...);
T t;
call_type c;
functor(T t, call_type c) : t(t), c(c) {}
res_type operator()(arg_types... args) { return (t.*c)(args...); }
};
Please note that this is only a stub, and not part of the code I would like to be reviewed.
My attempted contribution is a factory method, make_functor
, which should take care of deducing any template arguments where possible. In other cases (particularly overloaded functions) it should still allow users to specify the necessary template arguments directly. In practical terms, I would like it to satisfy the following testbed (also not part of the code I would like reviewed, though suggestions for extra testcases are always welcome):
bool test() {
bool success = true;
// C-style function
auto f1 = make_functor(mult);
success &= ( f1(2, 3) == 6 );
// C-style function pointer
int(*pf)(int, int) = mult;
auto f2 = make_functor(pf);
success &= ( f2(3, 3) == 9 );
// Generic functor with overloaded operator()
doubler d;
auto f3 = make_functor<doubler, int, int>(d);
success &= ( f3(2) == 4 );
// Member function pointer
container c;
auto f4 = make_functor(c, &container::triple);
success &= ( f4(4) == 12 );
// Just making sure none of the above breaks the copy-ctor
auto f5(f4);
success &= ( f5(4) == 12 );
return success;
}
What I've come up with so far is the following:
// C-style function pointers
template<typename res_type, typename... arg_types>
functor<res_type(*)(arg_types...), res_type, arg_types...>
make_functor(res_type(*c)(arg_types...)) {
return functor<res_type(*)(arg_types...), res_type, arg_types...>(c);
}
// Member function pointers
template<typename call_type, typename res_type, typename... arg_types>
functor<res_type(call_type::*)(arg_types...), res_type, arg_types...>
make_functor(call_type c, res_type(call_type::*f)(arg_types...)) {
return functor<res_type(call_type::*)(arg_types...), res_type, arg_types...>(c, f);
}
// Generic functor helper
template<typename call_type, typename res_type, typename... arg_types>
functor<call_type, res_type, arg_types...>
make_functor_impl(call_type c, res_type(call_type::*)(arg_types...)) {
return functor<call_type, res_type, arg_types...>(c);
}
// Generic functors
template<typename call_type>
auto make_functor(call_type c) -> decltype(make_functor_impl(c, &call_type::operator())) {
return make_functor_impl(c, &call_type::operator());
}
// Overloaded generic functors
template<typename call_type, typename res_type, typename... arg_types>
functor<call_type, res_type, arg_types...>
make_functor(call_type c) {
return functor<call_type, res_type, arg_types...>(c);
}
This seems to work well with the above testbed, and a few other things I threw at it. But I still have a few concerns, and would appreciate it if someone could take a look at the following areas:
- Maintainability. While the main goal is to shield the end-user from all the ugly, I'm still kinda hoping the factory itself might be made a little more readable.
- References to temporaries. I think this is safe from dangling references, but I'm not 100% sure.
I am aware that the indentation in the code as posted is a little haphazard. This is just an attempt to keep things a little more readable here.