I see no way to go ahead without using any kind of helper template to determine the parameter list here!
So the following solution is still based on Is it possible to figure out the parameter type and return type of a lambda?
For having function pointers and callable classes like lambdas, it only needs an specialized template instance.
template <typename CLASS>
struct function_traits_impl
: public function_traits_impl<decltype(&CLASS::operator())>
{};
template <typename CLASS, typename RET, typename... ARGS>
struct function_traits_impl< RET(CLASS::*)(ARGS...) const>
{
using args_type = std::tuple<ARGS...>;
using ret_type = RET;
};
template <typename CALLABLE > struct function_traits: public function_traits_impl< CALLABLE >{};
template< typename RET, typename... ARGS >
struct function_traits< RET(*)(ARGS...) >
{
using args_type = std::tuple<ARGS...>;
using ret_type = RET;
};
template < typename CLASS, typename CONTAINER, typename RET, typename ... ARGS> struct task;
template< typename CLASS, typename CONTAINER, typename RET, typename ... ARGS >
struct task< CLASS, CONTAINER, RET, std::tuple<ARGS...> >
{
using FUNC = std::function< RET(ARGS...)>;
FUNC func;
CONTAINER cont;
task( FUNC _func, CONTAINER& _cont): func{_func}, cont{_cont}
{
static_assert(
std::is_same<
//decltype( func( std::declval<PARMS>()...) ), // but is already known from given template parms!
RET,
typename CONTAINER::value_type
>::value,
"wrong return type, did not match with container type"
);
}
};
template <typename FUNC, typename CONTAINER >
task(FUNC, CONTAINER) -> task< FUNC, CONTAINER, typename function_traits<FUNC>::ret_type, typename function_traits<FUNC>::args_type>;
int Any( int ) { return 0; }
float WrongAny( int, int ) { return 1.1; }
int main()
{
std::vector<int> v;
//task t1{ [](int, int)->float { return 0; } , v}; // fails with assert as expected
task t2{ [](int, int)->int { return 0; } , v}; //Works!
task t3{ &Any , v}; // Works
//task t4{ &WrongAny, v }; fails as expected
}
This solution simply uses user defined deduction guide to forward the found parms from the trait which is helpful as you also use c++17.
Hint:
Generic lambdas cant be used, because if the parameters to call the lambda are unknown, how you could determine the parameters "automatically". It is quite easy to specify the parameters with the call and get the return type, but passing an generic lambda or an object with overloaded call operator needs to specify which of the functions/methods are should be used. So if you need generic lambdas or overloaded methods in class objects simply specify params manually! There can't be a trick in any language which allows you to give a set of optional calls and determine automatically which call should be used if no other information is available. As said: If params for the call are present, simply use them!
Remark:
If you use this solution, you only get a single template instance for all calls with same parameter set to the function call which may save some memory ;) But it uses a std::function to store teh callable which takes some runtime... You have now two solutions which differs in the results but both are usable ;)