Простой функциональный объект, служащий заменой std::function
Как написать функтор Func, чтобы вместо его экземпляра можно было передать и лямбду, не используя std::function<void()>?
Например, написав функцию:
void exec(Func f)
{
f();
}
Вызвать ее так:
exec([]() {std::cout << "Hello world\n"; });
Не интересно как можно менять сигнатуру функции, чтобы она принимала и лямбду, а интересно как написать функтор, чтобы можно было передать лямбду в функцию с приведенной сигнатурой.( спортивный интерес).
Ответы (2 шт):
Автор решения: HolyBlackCat
→ Ссылка
Максимально простой вариант:
class Func
{
struct Wrapper
{
virtual void invoke() = 0;
virtual ~Wrapper() {}
};
std::unique_ptr<Wrapper> wrapper_ptr;
public:
Func() {}
template <
typename T,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, Func>>
>
Func(T &&obj)
{
using type = std::decay_t<T>;
struct ThisWrapper : Wrapper
{
type obj;
ThisWrapper(const type &obj) : obj(obj) {}
ThisWrapper(type &&obj) : obj(std::move(obj)) {}
void invoke() override
{
return obj();
}
};
wrapper_ptr = std::make_unique<ThisWrapper>(std::forward<T>(obj));
}
void operator()()
{
wrapper_ptr->invoke();
}
};
Более правильный вариант, с передачей параметров и возвратом значения, и с возможностью копирования.
По-хорошему нужно еще сделать так, чтобы для маленьких лямбд не выделять память в куче, тогда будет совсем как std::function.
#include <memory>
#include <type_traits>
#include <utility>
template <typename>
class Func;
template <typename R, typename ...P>
class Func<R(P...)>
{
struct Wrapper;
using wrapper_ptr_t = std::unique_ptr<Wrapper>;
wrapper_ptr_t wrapper_ptr;
struct Wrapper
{
virtual wrapper_ptr_t clone() const = 0;
virtual R invoke(P ... params) = 0;
virtual ~Wrapper() {}
};
public:
Func() {}
Func(const Func &other)
: wrapper_ptr(other ? other.wrapper_ptr->clone() : wrapper_ptr_t{})
{}
Func(Func &&other) noexcept = default;
Func &operator=(const Func &other)
{
Func copy(other);
std::swap(wrapper_ptr, copy.wrapper_ptr);
return *this;
}
Func &operator=(Func &&other) noexcept = default;
template <
typename T,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<T>, Func> && std::is_convertible_v<
decltype(std::declval<std::decay_t<T> &>()(std::declval<P>()...)),
R
>
>
>
Func(T &&obj)
{
using type = std::decay_t<T>;
struct ThisWrapper : Wrapper
{
type obj;
ThisWrapper(const type &obj) : obj(obj) {}
ThisWrapper(type &&obj) : obj(std::move(obj)) {}
wrapper_ptr_t clone() const override
{
return std::make_unique<ThisWrapper>(obj);
}
R invoke(P ... params) override
{
return obj(std::forward<P>(params)...);
}
};
wrapper_ptr = std::make_unique<ThisWrapper>(std::forward<T>(obj));
}
explicit operator bool() const
{
return bool(wrapper_ptr);
}
template <typename ...Params>
R operator()(Params ... params)
{
return wrapper_ptr->invoke(std::forward<Params>(params)...);
}
};
Использование:
#include <iostream>
void exec(Func<void()> f)
{
f();
}
int main()
{
exec([]() {std::cout << "Hello world\n"; });
}