lundi 1 juillet 2019

C++11 method to check whether template arguments are "invocable"

C++17 introduced std::invocable, but I'm wondering about C++11-only solution. My first thought is to use std::result_of.

My current code looks like this:

template<typename Function, typename... Args,
    typename = typename std::result_of<Function&&(Args&&...)>::type>
int start(size_t stackSize, size_t queuedSignals, size_t signalActions,
    uint8_t priority, SchedulingPolicy schedulingPolicy, Function&& function,
    Args&&... args)
{
  // main implementation
}

template<typename Function, typename... Args,
    typename = typename std::result_of<Function&&(Args&&...)>::type>
int start(size_t stackSize, size_t queuedSignals, size_t signalActions,
    uint8_t priority, Function&& function, Args&&... args)
{
  return start(stackSize, queuedSignals, signalActions, priority,
      SchedulingPolicy::roundRobin, std::forward<Function>(function),
      std::forward<Args>(args)...);
}

template<typename Function, typename... Args,
    typename = typename std::result_of<Function&&(Args&&...)>::type>
int start(size_t stackSize, uint8_t priority, Function&& function,
Args&&... args)
{
  return start(stackSize, 0, 0, priority, SchedulingPolicy::roundRobin,
      std::forward<Function>(function), std::forward<Args>(args)...);
}

template<typename Function, typename... Args,
    typename = typename std::result_of<Function&&(Args&&...)>::type>
int start(size_t stackSize, uint8_t priority,
    SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args)
{
  return start(stackSize, 0, 0, priority, schedulingPolicy,
      std::forward<Function>(function), std::forward<Args>(args)...);
}

There is basically one main implementation - with all possible arguments - and 3 wrappers - which don't have all arguments. I use SFINAE in the template arguments - typename = typename std::result_of<Function&&(Args&&...)>::type - as otherwise one of the wrappers causes infinite recursion. This seems to work fine, but I did only very limited testing. However on cppreference I've read the motivation for depreciation of std::result_of in favor of std::invoke_result (BTW the info there is why I'm using r-value references instead of std::result_of<Function(Args...)>), which made me wonder whether my use is correct or maybe there will be (real-life) situations where this will actually fail even though the passed arguments are correct? My use-case is passing all possible "callables": regular functions, member functions, lambdas or functors.

Aucun commentaire:

Enregistrer un commentaire