1
votes

I want to save a generic callable with its state for later use. Please see the example code bellow. I could probably use std::function or std::bind to achieve this but I do not know what is best. Also please note that in the main() of the example below, the capturedInt must be saved in the state of the callable.

What are the possibilities for:

  • makeCallable(fun, args...) { ... }
  • CallableType
template <typename RetT>
class Service
{
public:

   template <typename Fn, typename... Args>
   Service(Fn&& fun, Args&&... args)
   {
      m_callable = makeCallable(fun, args...);
   }

   Run()
   {
      m_callable();
   }

   CallableType<RetT> m_callable;
};

// Template deduction guides (C++17)
template <typename Fn, typename... Args>
Service(Fn&& fun, Args&&... args) -> Service<std::invoke_result_t<std::decay_t<Fn>, std::decay_t<Args>...>>;

int main()
{
   Service* s = nullptr;
   {
      int capturedInt = 5;
      s = new Service([capturedInt]() { std::cout << capturedInt << std::endl; } );
   }
   
   s->Run();
}

2

2 Answers

1
votes

I would also use std::function, but leave it as the interface to the class, like this:

template <typename RetT>
class Service
{
public:

   Service(std::function<RetT()> fun)
   {
      m_callable = std::move(fun);
   }

   Run()
   {
      m_callable();
   }
private:
   std::function<RetT()> m_callable;
};

Then you're explicit about the options to store callables for the class. The user can then decide how to bind their arguments to the callable themselves, which with std::function is flexible.

s = new Service([capturedInt]() { std::cout << capturedInt << std::endl; } );
s->Run();

or

struct Foo
{
    void MyPrint(int capturedInt) { std::cout << capturedInt << std::endl; }
};
Foo foo;
int capturedInt = 5;
s = new Service(std::bind(&Foo::MyPrint, &foo, capturedInt);
s->Run();

. Then you don't have to worry about life time problems caused by the class.

0
votes

Given the setup, your only option for m_callable would be an std::function. Since the type of the functor is an argument to the constructor itself, you'd have to type-erase the functor to save it for future use - and std::function is just a mechanism for this.

As a result, m_callable would be:

std::function<retT ()> m_callable;

and you would set it like that:

m_callable = [=]() { return fun(args...); }