Igie Mañalac
Igie Mañalac

Reputation: 23

C++ Implementing a timeout function template for class methods

The goal that I want to achieve is for the program to stop the process and return a timeout error if the process exceeds the timeout time.

I got a timeout function from the second most voted answer to this question. I ran into an error which says that std::result_of_t is deprecated in c++17, so I re-created it using the suggested replacement std::invoke_result_t and implemented it like so:

template <typename TF, typename TDuration, class... TArgs>
std::invote_result_t<TF&&, TArgs&&...> run_with_timeout(TF&& f, TDuration timeout, TArgs&&... args)
{
    using R = std::invoke_result_t<TF&&, TArgs&&...>;
    std::packaged_task<R(TArgs...)> task(f);
    auto future = task.get_future();
    std::thread thr(std::move(task), std::forward<TArgs>(args)...);
    if (future.wait_for(timeout) != std::future_status::timeout)
    {
       thr.join();
       return future.get(); // this will propagate exception from f() if any
    }
    else
    {
       thr.detach(); // we leave the thread still running
       throw std::runtime_error("Timeout");
    }
}

I wanted to use it to check for timeout on a class method's function. So I tried to use it in a way similar to:

template <typename TF, typename TDuration, class... TArgs>
std::invote_result_t<TF&&, TArgs&&...> ClassOne::run_with_timeout(TF&& f, TDuration timeout, TArgs&&... args)
{
    using R = std::invoke_result_t<TF&&, TArgs&&...>;
    std::packaged_task<R(TArgs...)> task(f);
    auto future = task.get_future();
    std::thread thr(std::move(task), std::forward<TArgs>(args)...);
    if (future.wait_for(timeout) != std::future_status::timeout)
    {
       thr.join();
       return future.get(); // this will propagate exception from f() if any
    }
    else
    {
       thr.detach(); // we leave the thread still running
       throw std::runtime_error("Timeout");
    }
}

// The function checked for timeout
int ClassOne::func(ClassTwo *param1, std::string name)
{
    // Some code here...

    // For mimicking function process:
    std::this_thread::sleep_for(10s);
    return 0;
}

// Function which calls the timed process with timeout function
int ClassOne::dummy(ClassTwo *param1, std::string name)
{
    int retVal = 0; // zero for no error, non-zero for error
    try
    {
        retVal = run_with_timeout(func, 20s, param1, name);
    }
    catch (runtime_error & e)
    {
        return 1;
    }
}

With this, I get the error:

no instance of function template "ClassOne::run_with_timeout" matches the argument list
    argument types are: (int (ClassTwo *param1, std::string target), std::chrono::seconds, ClassTwo *, std::string)

I think the problem is something similar to this entry but I have no idea how to correct it. Some functions that I need to use the timeout function on have different object parameters which is why a function template is used.

Thank you very much in advance for the help!

Upvotes: 1

Views: 571

Answers (1)

Botje
Botje

Reputation: 30860

As the name suggests, std::invoke_result_t is the result type of applying std::invoke. I highlighted the case that applies to you here:

INVOKE(f, t1, t2, ..., tN) is defined as follows:
...
If f is a pointer to member function of class T:

  • If std::is_base_of<T, std::decay_t<decltype(t1)>>::value is true, then INVOKE(f, t1, t2, ..., tN) is equivalent to (t1.*f)(t2, ..., tN)
  • If t1 does not satisfy the previous items, then INVOKE(f, t1, t2, ..., tN) is equivalent to ((*t1).*f)(t2, ..., tN).

So your call should be:

retVal = run_with_timeout(&ClassOne::func, 20s, this, std::move(param1), std::move(name));

EDIT: I struggled for a good twenty minutes with getting it to actually work. Adding std::move as above OR taking name by const reference (in dummy) makes it compile. I cannot think of a similar transformation for param1. I would be very interested to know the reason behind that error, but at least your immediate problem is solved.

Upvotes: 1

Related Questions