Pull to refresh

Comments 20

В коде второго примера заметил опечатку:
using T1 = typename TypeAt<0u, Arguments...>::Type;

Вместо той сторки должна была быть эта:
using T1 = typename TypeAt<1u, Arguments...>::Type;
new (Storage.data()) Callable(std::move(Fn));


а деструктор у Callable вызывать не нужно? Кроме того, чем вам type erasure не угодил, зачем в vector-е конструировать Callable (может у меня вкус плохой, но мне конкретно этот момент не кажется элегантным решением)?
Я это вижу как-то так:
Код
#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));
}

Вы используете forwarding-references, но для случая `const 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`.
Как раз об этом подумал, но вы меня обогнали. Еще нужно поправить и в конструкторе. Но обычный 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));
}

template <typename T>
struct call_type : call_type<decltype(&std::remove_reference_t< T >::operator())>
{};

Я бы сделал так.
Да, но нам еще нужно вывести тип функтора в конструкторе, и при этом сохранить ссылки на обычные функции.
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)))
{}
// Какой должна быть сигнатура функции, принимающей такой аргумент?
someOtherFunction(Lambda);

void someOtherFunction(decltype(Lambda) lam);
template<typename F> return_type someOtherFunction(F&& f)
но передать ее потом куда-то не используя шаблонов затруднительно.
Один мой знакомый подкинул мне интересную задачку: нужно вызвать функцию через указатель и передать в нее предварительно сохраненные аргументы. Обязательным условием было не использовать std::function.

Вероятно, я чего-то не понял, но почему нельзя было именно это и сделать? То есть, создать просто указатель на функцию и вызвать функцию через него?
я так понимаю, что стояла задача потрахаться и под страхом смерти не использовать С
Просто в условиях я этого не увидел, вот и спросил.
вызвать функцию через указатель и передать в нее предварительно сохраненные аргументы

Я себе это вижу так:
#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;
}

Это уже не тот C++, который я знал и любил :(
Просто чел обожает лечить зубы перректально. Новый стандарт призван сделать код более чистым, кратким и понятным, а не для того, чтобы воротить горы трубочек, колесиков и рычажков для тривиальной вещи, прямо поддерживаемой языком.
Чтобы не быть голословным:

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 строчки!
UFO just landed and posted this here
Лямбду можно вызвать, скопировать, привести к функции (если она без связанных переменных)… и, внезапно, привести к std::function.
Если std::function не проходит через слишком узкие ограничения «мой приятель мне запретил», — ну какие проблемы, копируем минимум-миниморум из стандартной библиотеки, переименовываем и получаем то же самое, только без костылей и велосипедов.

Но, видимо, каждый программист должен написать велосипедную версию std::function, а в перспективе — велосипедные форт и лисп. (Точнее, написать велосипедный лисп — это не долженствование, а неизбежность).
Sign up to leave a comment.

Articles