1

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!

  • Is your `func` static? If not you need to pass a pointer to a `ClassOne` object (eg `this`) as first parameter. – Botje Oct 06 '20 at 08:46
  • @Botje `func` is not static. If it's not too much, can you elaborate more with an example with above code as reference? What will run_with_timeout function template look like? How will I be able to invoke ClassOne::func in run_with_timeout? `template std::invote_result_t ClassOne::run_with_timeout(TC *className, TF&& f, TDuration timeout, TArgs&&... args)` What's the mistake for the above code? Thank you very much! – Igie Mañalac Oct 07 '20 at 00:11

1 Answers1

1

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.

Botje
  • 26,269
  • 3
  • 31
  • 41
  • Could you please add the `std::bind` and lambda solutions? I'm sorry these are all new to me. I tried your suggested solution but it still outputs the same error. I tried `retVal = run_with_timeout(&func, 20s, this, param1, name);` (using `this` or `*this`) and it eliminates the intellisense error but it still outputs the same error after build. Passing func as is outputs the error `use '&' to create a pointer to member` and passing `&func` outputs `'&': illegal operation on bound member function expression`. – Igie Mañalac Oct 08 '20 at 02:20
  • Wow it works now! Thank you very much @Botje!! What are the best references to study these if I may ask? I would like to learn more about it. – Igie Mañalac Oct 09 '20 at 00:05