Функция-обертка, которая работает для всех видов функторов без приведения

Я хотел бы создать функцию, которая принимает слабый указатель и любой вид функтора (лямбда, std::function, что угодно) и возвращает новый функтор, который выполняет оригинальный функтор только тогда, когда указатель не был удален за это время (поэтому давайте предположим, что есть WeakPointer типа с такой семантикой). Все это должно работать для любого функтора без необходимости явно указывать сигнатуру функтора через параметры шаблона или приведение.

РЕДАКТИРОВАТЬ:
Некоторые комментаторы указали, что std::function — который я использовал в своем подходе — может вообще не понадобиться, равно как и лямбда (хотя в своем первоначальном вопросе я также забыл упомянуть, что мне нужно захватить параметр слабого указателя), поэтому любое альтернативное решение, которое решает общую проблему это, конечно, также высоко ценится, может быть, я не думал достаточно нестандартно и был сосредоточен на использовании лямбда + std::function, В любом случае, здесь идет то, что я пытался до сих пор:

template<typename... ArgumentTypes>
inline std::function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const std::function<void(ArgumentTypes...)>&& fun)
{
return [=] (ArgumentTypes... args)
{
if(pWeakPointer)
{
fun(args...);
}
};
}

Это хорошо работает без необходимости явно указывать типы аргументов, если я передаю std::function, но терпит неудачу, если я передаю лямбда-выражение. Я думаю, это потому, что std::function Неоднозначность конструктора, как в этот вопрос. В любом случае я попробовал следующий помощник, чтобы иметь возможность захватывать любые виды функций:

template<typename F, typename... ArgumentTypes>
inline function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const F&& fun)
{
return wrap(pWeakPointer, std::function<void(ArgumentTypes...)>(fun));
}

Теперь это работает для лямбд, которые не имеют параметров, но не работают для других, так как он всегда создает экземпляры ArgumentTypes... с пустым набором.

Я могу придумать два решения проблемы, но не смог реализовать ни одно из них:

  1. Убедитесь, что правильно std::function (или другой тип помощника Functor) создается для лямбды, то есть лямбда с подписью R(T1) приводит к std::function(R(T1)) таким образом ArgumentTypes... будет правильно выведен
  2. Не ставьте ArgumentTypes... в качестве параметра шаблона вместо этого есть какой-то другой способ (boost?), чтобы получить пакет аргументов из лямбда / функтора, так что я мог бы сделать что-то вроде этого:

template<typename F>
inline auto wrap(WeakPointer pWeakPointer, const F&& fun) -> std::function<void(arg_pack_from_functor(fun))>
{
return wrap(pWeakPointer, std::function<void(arg_pack_from_functor(fun))(fun));
}

1

Решение

Предполагая, что слабый указатель занимает место первого аргумента, вот как я бы сделал это с общей лямбдой (с перехватами хода) и если бы C ++ позволил бы мне вернуть такую ​​лямбду:

template<typename Functor, typename Arg, typename... Args>
auto wrap(Functor&& functor, Arg&& arg)
{
return [functor = std::forward<Functor>(functor)
, arg = std::forward<Arg>(arg)]<typename... Rest>(Rest&&... rest)
{
if(auto e = arg.lock()) {
return functor(*e, std::forward<Rest>(rest)...);
} else {
// Let's handwave this for the time being
}
};
}

Этот гипотетический код можно перевести в реальный код C ++ 11, если мы вручную «развернем» универсальную лямбду в полиморфный функтор:

template<typename F, typename Pointer>
struct wrap_type {
F f;
Pointer pointer;

template<typename... Rest>
auto operator()(Rest&&... rest)
-> decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )
{
if(auto p = lock()) {
return f(*p, std::forward<Rest>(rest)...);
} else {
// Handle
}
}
};

template<typename F, typename Pointer>
wrap_type<typename std::decay<F>::type, typename std::decay<Pointer>::type>
wrap(F&& f, Pointer&& pointer)
{ return { std::forward<F>(f), std::forward<Pointer>(pointer) }; }

Существует два простых варианта обработки случая, когда срок действия указателя истек: либо распространить исключение, либо вернуть внеполосное значение. В последнем случае тип возврата станет, например, optional<decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )> а также // Handle станет return {};,

Пример кода, чтобы увидеть все в действии.

[Упражнение для амбициозного: улучшите код, чтобы можно было использовать auto g = wrap(f, w, 4); auto r = g();, Затем, если это еще не так, улучшите его так, чтобы auto g = wrap(f, w1, 4, w5); также возможно и «делает правильные вещи». ]
1

Другие решения

Вам не нужно использовать лямбду.

#include <iostream>
#include <type_traits>

template <typename F>
struct Wrapper {
F f;

template <typename... T>
auto operator()(T&&... args) -> typename std::result_of<F(T...)>::type {
std::cout << "calling f with " << sizeof...(args) << " arguments.\n";
return f(std::forward<T>(args)...);
}
};

template <typename F>
Wrapper<F> wrap(F&& f) {
return {std::forward<F>(f)};
}

int main() {
auto f = wrap([](int x, int y) { return x + y; });
std::cout << f(2, 3) << std::endl;
return 0;
}
3

По вопросам рекламы [email protected]