Function
One of my favourate additions to C++11 was std::function because it provides
a very elegant solution on how to run something either at a later point in time
or on a different context (like a thread).
In C++03 you would have to use something like this.
class Fn
{
protected:
virtual ~Fn() {}
virtual exec() = 0;
};
You would then create a derives class of Fn on the heap and pass the pointer
to the context you want to execute. Finally you would delete your pointer to
Fn
C++11 makes things much easier. All you do is pass a std::function. A
std::function can be initialized using std::bind or better, using a lamda
with capture. Any data remains captured until required.
std::function does have a downside, I cannot capture a std::unique_ptr.
This is cause std::function is copyable and std::unique_ptr is not.
This can be solved using our own Function object looking something like:
#include <utility>
#include <type_traits>
template <typename R, typename... Args>
class CallBase
{
public:
virtual ~CallBase() {}
virtual R call(Args... args) = 0;
};
template <typename F, typename R, typename... Args>
class Call : public CallBase<R,Args...>
{
public:
Call(F f)
: m_f{ std::forward<F>(f) }
{}
R call(Args ...args) override {
return m_f(std::forward<Args>(args)...);
}
private:
F m_f;
};
template <typename Sig>
class Function;
template <typename R, typename... Args>
class Function<R(Args...)>
{
public:
Function()
: m_call(nullptr)
{}
Function(std::nullptr_t)
: m_call(nullptr)
{}
template <typename F>
Function (F f)
: m_call (new Call<F, R, Args...>(std::forward<F>(f)))
{
static_assert(
std::is_same< typename
std::invoke_result<F,Args...>::type, R
>::value, "Return type must match function");
}
Function(Function&& rhs) noexcept
: m_call{ rhs.m_call }
{
rhs.m_call = nullptr;
}
void swap(Function& rhs) noexcept{
std::swap(this->m_call, rhs.m_call);
}
Function& operator=(const Function&) = delete;
Function& operator=(Function&& rhs) noexcept
{
m_call = rhs.m_call;
rhs.m_call = nullptr;
}
~Function()
{
delete m_call;
}
operator bool() const noexcept
{
return m_call != nullptr;
}
R operator()(Args ... args)
{
return m_call->call(std::forward<Args>(args)...);
}
bool operator==(const Function& rhs) const noexcept
{
return m_call == rhs.m_call;
}
private:
CallBase<R, Args...>* m_call;
};
There is nothing like the std::bind interface but that is ok since you can
get everything you need from lambdas as illustated. Just note that move
captures only works from c++14.