Comments 20
В коде второго примера заметил опечатку:
Вместо той сторки должна была быть эта:
using T1 = typename TypeAt<0u, Arguments...>::Type;
Вместо той сторки должна была быть эта:
using T1 = typename TypeAt<1u, Arguments...>::Type;
0
new (Storage.data()) Callable(std::move(Fn));
а деструктор у Callable вызывать не нужно? Кроме того, чем вам type erasure не угодил, зачем в vector-е конструировать Callable (может у меня вкус плохой, но мне конкретно этот момент не кажется элегантным решением)?
0
Я это вижу как-то так:
Код
#include <tuple>
#include <memory>
#include <cassert>
namespace detail {
template <typename T>
struct call_type : call_type<decltype(&T::operator())>
{};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) volatile > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const volatile > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) &> : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const & > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) volatile & > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const volatile & > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) && > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const && > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) volatile && > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const volatile && > : call_type<ResultType(Args...)> {};
template <typename ResultType, typename ... Args>
struct call_type<ResultType(*)(Args...)> : call_type<ResultType(Args...)> {};
template <typename ResultType, typename ... Args>
struct call_type<ResultType(&)(Args...)> : call_type<ResultType(Args...)> {};
template <typename ResultType, typename ... Args>
struct call_type<ResultType(Args...)>
{
using type = ResultType(Args...);
};
template <class R, class ... Args>
struct any_function {
virtual ~any_function() {};
virtual R invoke(Args ... args)=0;
virtual std::unique_ptr<any_function<R,Args...>> clone() const = 0;
};
template <class F, class R, class ... Args>
struct concrete_function : any_function<R, Args...> {
template <class Q>
concrete_function(Q&& f)
: f_(std::forward<Q>(f))
{};
R invoke(Args ... args) override {
return f_(std::forward<Args>(args)...);
}
std::unique_ptr<any_function<R,Args...>> clone() const override {
return std::make_unique<concrete_function<F,R,Args...>>(f_);
}
private:
F f_;
};
}
template <class Function>
struct function;
template <class R, class ... Args>
struct function<R(Args...)> {
using self_type = function<R(Args...)>;
using result_type = R;
using argument_types = std::tuple<Args...>;
function() {};
function(self_type&& other)
: ptr(std::move(other.ptr))
{}
function(const self_type& other)
: ptr(other.clone())
{}
self_type& operator=(const self_type& other) {
ptr = other.clone();
return *this;
}
self_type& operator=(self_type&& other) {
ptr = std::move(other.ptr);
return *this;
}
template <class F>
function(F&& f)
: ptr(std::make_unique<detail::concrete_function<F,R,Args...>>(std::forward<F>(f)))
{}
R operator()(Args ... args) {
assert(ptr);
return ptr->invoke(std::forward<Args>(args)...);
}
private:
std::unique_ptr<detail::any_function<R,Args...>> clone() const {
if(ptr) {
return ptr->clone();
} else {
return nullptr;
}
}
std::unique_ptr<detail::any_function<R,Args...>> ptr;
};
template<class F>
decltype(auto) make_function(F&& f) {
return function<typename detail::call_type<F>::type>(std::forward<F>(f));
}
+2
Вы используете forwarding-references, но для случая `const T &` код не сработает:
Такой user-код даёт hard-error:
Необходимо использовать `std::remove_reference_t`.
template <typename T> struct call_type : call_type<decltype(&T::operator())> {};
Такой user-код даёт hard-error:
struct A { void operator () () const { ; } }; A const a{}; auto f = make_function(a);
Необходимо использовать `std::remove_reference_t`.
0
Как раз об этом подумал, но вы меня обогнали. Еще нужно поправить и в конструкторе. Но обычный std::remove_reference_t не подойдет, тогда не получится использовать простые функции. Напишем свой:
Код
#include <tuple>
#include <memory>
#include <cassert>
namespace detail {
template <typename T>
struct call_type : call_type<decltype(&T::operator())>
{};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) volatile > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const volatile > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) &> : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const & > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) volatile & > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const volatile & > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) && > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const && > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) volatile && > : call_type<ResultType(Args...)> {};
template <typename ClassType, typename ResultType, typename ... Args>
struct call_type<ResultType(ClassType::*)(Args...) const volatile && > : call_type<ResultType(Args...)> {};
template <typename ResultType, typename ... Args>
struct call_type<ResultType(*)(Args...)> : call_type<ResultType(Args...)> {};
template <typename ResultType, typename ... Args>
struct call_type<ResultType(&)(Args...)> : call_type<ResultType(Args...)> {};
template <typename ResultType, typename ... Args>
struct call_type<ResultType(Args...)>
{
using type = ResultType(Args...);
};
template <typename T>
struct remove_reference {
using type = T;
};
template <typename ResultType, typename ... Args>
struct remove_reference<ResultType(&)(Args...)> {
using type = ResultType(&)(Args...);
};
template <typename T>
struct remove_reference <T&> {
using type = T;
};
template <class R, class ... Args>
struct any_function {
virtual ~any_function() {};
virtual R invoke(Args ... args)=0;
virtual std::unique_ptr<any_function<R,Args...>> clone() const = 0;
};
template <class F, class R, class ... Args>
struct concrete_function : any_function<R, Args...> {
template <class Q>
concrete_function(Q&& f)
: f_(std::forward<Q>(f))
{};
R invoke(Args ... args) override {
return f_(std::forward<Args>(args)...);
}
std::unique_ptr<any_function<R,Args...>> clone() const override {
return std::make_unique<concrete_function<F,R,Args...>>(f_);
}
private:
F f_;
};
}
template <class Function>
struct function;
template <class R, class ... Args>
struct function<R(Args...)> {
using self_type = function<R(Args...)>;
using result_type = R;
using argument_types = std::tuple<Args...>;
function() {};
function(self_type&& other)
: ptr(std::move(other.ptr))
{}
function(const self_type& other)
: ptr(other.clone())
{}
self_type& operator=(const self_type& other) {
ptr = other.clone();
return *this;
}
self_type& operator=(self_type&& other) {
ptr = std::move(other.ptr);
return *this;
}
template <class F>
function(F&& f)
: ptr(std::make_unique<detail::concrete_function<typename detail::remove_reference<F>::type,R,Args...>>(std::forward<F>(f)))
{}
R operator()(Args ... args) {
assert(ptr);
return ptr->invoke(std::forward<Args>(args)...);
}
private:
std::unique_ptr<detail::any_function<R,Args...>> clone() const {
if(ptr) {
return ptr->clone();
} else {
return nullptr;
}
}
std::unique_ptr<detail::any_function<R,Args...>> ptr;
};
template<class F>
decltype(auto) make_function(F&& f) {
return function<typename detail::call_type<typename detail::remove_reference<F>::type>::type>(std::forward<F>(f));
}
0
template <typename T> struct call_type : call_type<decltype(&std::remove_reference_t< T >::operator())> {};
Я бы сделал так.
0
// Какой должна быть сигнатура функции, принимающей такой аргумент? someOtherFunction(Lambda);
void someOtherFunction(decltype(Lambda) lam);
0
Один мой знакомый подкинул мне интересную задачку: нужно вызвать функцию через указатель и передать в нее предварительно сохраненные аргументы. Обязательным условием было не использовать std::function.
Вероятно, я чего-то не понял, но почему нельзя было именно это и сделать? То есть, создать просто указатель на функцию и вызвать функцию через него?
+4
ещё один вариант — Parameter Object design pattern.
У данного подхода большой плюс — не зависит от языка программирования.
У данного подхода большой плюс — не зависит от языка программирования.
-1
вызвать функцию через указатель и передать в нее предварительно сохраненные аргументы
Я себе это вижу так:
#include <utility> template< typename ...types > auto make_caller(types &&... _values) { return [_values...] (auto && callee) mutable -> decltype(auto) { return std::forward< decltype(callee) >(callee)(std::forward< types >(_values)...); }; } // main.cpp #include <iostream> #include <cstdlib> struct A { template< typename ...types > void operator () (types...) const & { std::cout << __PRETTY_FUNCTION__ << std::endl; } template< typename ...types > void operator () (types...) && { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; void f(int, float, double, long double) { std::cout << __PRETTY_FUNCTION__ << std::endl; } int main() { auto caller = make_caller(1, 1.0f, 1.0, 1.0L); caller(A{}); A const a{}; caller(a); caller(f); caller(&f); // в точности то, что нужно return EXIT_SUCCESS; }
0
Это уже не тот C++, который я знал и любил :(
-4
Просто чел обожает лечить зубы перректально. Новый стандарт призван сделать код более чистым, кратким и понятным, а не для того, чтобы воротить горы трубочек, колесиков и рычажков для тривиальной вещи, прямо поддерживаемой языком.
+2
Чтобы не быть голословным:
int p1 = 1, p2 = 2;
typedef double (*FuncT)(int i1, int i2);
FuncT f1 = xyz; // тут имя любой фунции с той же сигнатурой.
double d1 = f1(p1, p2);
или, если сам вызоыв надо передать как параметр:
double myFunc() { return f1(p1, p2); }
или как-угодно еще, в зависимости от требований, но в 2-3 строчки!
int p1 = 1, p2 = 2;
typedef double (*FuncT)(int i1, int i2);
FuncT f1 = xyz; // тут имя любой фунции с той же сигнатурой.
double d1 = f1(p1, p2);
или, если сам вызоыв надо передать как параметр:
double myFunc() { return f1(p1, p2); }
или как-угодно еще, в зависимости от требований, но в 2-3 строчки!
+1
UFO just landed and posted this here
Лямбду можно вызвать, скопировать, привести к функции (если она без связанных переменных)… и, внезапно, привести к std::function.
Если std::function не проходит через слишком узкие ограничения «мой приятель мне запретил», — ну какие проблемы, копируем минимум-миниморум из стандартной библиотеки, переименовываем и получаем то же самое, только без костылей и велосипедов.
Но, видимо, каждый программист должен написать велосипедную версию std::function, а в перспективе — велосипедные форт и лисп. (Точнее, написать велосипедный лисп — это не долженствование, а неизбежность).
Если std::function не проходит через слишком узкие ограничения «мой приятель мне запретил», — ну какие проблемы, копируем минимум-миниморум из стандартной библиотеки, переименовываем и получаем то же самое, только без костылей и велосипедов.
Но, видимо, каждый программист должен написать велосипедную версию std::function, а в перспективе — велосипедные форт и лисп. (Точнее, написать велосипедный лисп — это не долженствование, а неизбежность).
+3
Sign up to leave a comment.
Передача сохраненных аргументов в функцию