FastDelegate и lambdas - не могу заставить их работать (самые быстрые делегаты Дона Клагстона)

Я пытаюсь создать реализацию C++11 Указатели функций-членов Дона Клагстона и самые быстрые делегаты C++, и заставить его работать как вставную замену std::function.

Вот что у меня получилось.

Я создаю лямбда FastDelegates следующим образом:

// FastFunc is my name for FastDelegate
template<typename LambdaType> FastFunc(LambdaType lambdaExpression)
{
    this->m_Closure.bindmemfunc(&lambdaExpression, &LambdaType::operator());
}

Теперь немного тестов:

FastFunc<void()> test = []{ std::cout << "hello" << std::endl; };
test();
// Correctly prints "hello"

bool b{false};
FastFunc<void()> test2 = [&b]{ std::cout << b << std::endl; };
test2();
// Crash!

Как видите, когда лямбда «тривиальна» (без захватов), копирование по значению и получение адреса работает. Но когда лямбда хранит какое-то состояние (захватывает), я не могу просто скопировать его по значению в файл FastFunc.

Я попытался получить лямбду по ссылке, но не могу этого сделать, когда она временная, как в примере.

Мне нужно каким-то образом сохранить лямбду внутри FastFunc, но я не хочу использовать std::shared_ptr, потому что это медленно (Я попробовал другую реализацию fastdelegate, которая использовала его, и его производительность была сравнима с std::function).

Как мне заставить мою реализацию максимально быстрых делегатов C++ Дона Клагстона работать с лямбда-выражениями, которые фиксируют состояние, сохраняя потрясающую производительность быстрых делегатов?


person Vittorio Romeo    schedule 05.09.2013    source источник
comment
Это потому, что вы не можете преобразовать захваченную лямбду в указатель на функцию: вам нужно стирание типа, и std::shared_ptr<void> предоставляет его.   -  person user1095108    schedule 05.09.2013
comment
@ user1095108: если вы скопируете std::function, инкапсулирующий изменяемую лямбду (также известную как лямбда с изменяемым состоянием), будут ли два экземпляра std::function иметь одно и то же состояние или у каждого будет своя копия?   -  person Matthieu M.    schedule 05.09.2013
comment
@MatthieuM. у каждого есть своя копия AFAIK.   -  person user1095108    schedule 05.09.2013
comment
@ user1095108: хорошо, тогда он, вероятно, не использует std::shared_ptr<void>, потому что стирание типа предотвратит копирование внутренней лямбды (там нет метода clone).   -  person Matthieu M.    schedule 05.09.2013
comment
@MatthieuM. Нет, он либо использует размещение new, либо копирует в newly выделенный блок. Хороший вопрос об изменчивых хе-хе-хе.   -  person user1095108    schedule 05.09.2013
comment
@MatthieuM. Вот почему, кстати, std::function часто приводит к катастрофе, если вы используете его с алгоритмами <algorithm>.   -  person user1095108    schedule 05.09.2013
comment
@ user1095108: Да, к сожалению, алгоритм был указан как принимающий итераторы и предикаты по значению. Это означает, что каждый раз, когда ваш итератор или предикат толстый, вы рискуете проблемами с производительностью :(   -  person Matthieu M.    schedule 06.09.2013
comment
Немного опоздал на вечеринку, но я единственный, кто заметил, что приведенный выше код берет адрес лямбда-выражения, который уничтожается, когда функция завершается, создавая таким образом оборванный указатель, который, вероятно, ответственен за сбой? Может быть, lambdaExpression следует брать по константной ссылке?   -  person SomeProgrammer    schedule 10.02.2021


Ответы (4)


Вы хорошо диагностировали ситуацию: вам нужно сохранить состояние.

Поскольку лямбда является временным объектом, вам фактически разрешено перемещаться из него (обычно), что должно быть предпочтительнее копирования, если это возможно (поскольку перемещение является более общим, чем копирование).

Теперь все, что вам нужно сделать, это зарезервировать для него некоторое хранилище, и если для этого требуется динамическое выделение, вы действительно можете получить снижение производительности. С другой стороны, объект должен иметь фиксированную площадь основания, так что?

Одно из возможных решений — предложить настраиваемую (но ограниченную) емкость хранилища:

static size_t const Size = 32;
static size_t const Alignment = alignof(std::max_align_t);

typedef std::aligned_storage<Size, Alignment>::type Storage;
Storage storage;

Теперь вы можете (при необходимости используя reinterpret_cast) сохранить свою лямбду в storage при условии, что ее размер подходит (что можно определить с помощью static_assert).

Наконец-то удалось получить работающий пример (пришлось перезапустить с нуля, потому что Бог такой подробный код быстрого делегата !!), вы можете увидеть его в действии здесь (и код ниже).

У меня есть только царапина на поверхности, особенно потому, что в ней отсутствуют операторы копирования и перемещения. Чтобы сделать это правильно, эти операции необходимо добавить в обработчик по тому же шаблону, что и две другие операции.

Код:

#include <cstddef>

#include <iostream>
#include <memory>
#include <type_traits>

template <typename, size_t> class FastFunc;

template <typename R, typename... Args, size_t Size>
class FastFunc<R(Args...), Size> {
public:
    template <typename F>
    FastFunc(F f): handler(&Get<F>()) {
        new (&storage) F(std::move(f));
    }

    ~FastFunc() {
        handler->destroy(&storage);
    }

    R operator()(Args&&... args) {
      return handler->apply(&storage, std::forward<Args>(args)...);
    }

private:
    using Storage = typename std::aligned_storage<Size, alignof(max_align_t)>::type;

    struct Handler {
        R (*apply)(void*, Args&&...);
        void (*destroy)(void*);
    }; // struct Handler

    template <typename F>
    static R Apply(void* f, Args&&... args) {
        (*reinterpret_cast<F*>(f))(std::forward<Args>(args)...);
    }

    template <typename F>
    static void Destroy(void* f) {
        reinterpret_cast<F*>(f)->~F();
    }

    template <typename F>
    Handler const& Get() {
        static Handler const H = { &Apply<F>, &Destroy<F> };
        return H;
    } // Get

    Handler const* handler;
    Storage storage;
}; // class FastFunc

int main() {
    FastFunc<void(), 32> stateless = []() { std::cout << "stateless\n"; };
    stateless();

    bool b = true;
    FastFunc<void(), 32> stateful = [&b]() { std::cout << "stateful: " << b << "\n"; };
    stateful();

    b = false;
    stateful();

    return 0;
}
person Matthieu M.    schedule 05.09.2013
comment
std::aligned_storage звучит великолепно! Однако у меня возникли проблемы с перемещением лямбды внутри хранилища. Можете ли вы показать простой пример? Те, что на cppreference, не помогли. - person Vittorio Romeo; 05.09.2013
comment
@VittorioRomeo Просто скопируйте или переместите его туда, например. new (&storage) LambdaType(lambdaExpression);. Но это не будет быстрее, так как storage местоположение теперь зависит от указателя this, что означает, что встраивание вызова теперь невозможно. - person user1095108; 05.09.2013
comment
@VittorioRomeo: я работал над этим, но этот бог быстрых делегатов был настолько ужасен, что я перешел с нуля к чему-то более простому. Возможно, это уже не так быстро (никаких гарантий производительности не подразумевается!), однако оно демонстрирует, как копировать/перемещать состояние. - person Matthieu M.; 05.09.2013
comment
Разве это не равнозначно повторной реализации std::function (и виртуальных функций тоже!)? - person R. Martinho Fernandes; 05.09.2013
comment
@R.MartinhoFernandes: Ну ... вот что такое быстрый делегат (хотя он был изобретен в то время, когда std::function не существовало). Однако есть небольшие различия: 1/ std::function использует выделение кучи (в целом), так что это потенциально быстрее, 2/он использует меньше места, чем использование виртуальных функций (потенциально), поскольку для виртуальных функций кажется, что вам нужно будет хранить как виртуальные указатель (в хранилище) и указатель на базовый класс (вне хранилища). - person Matthieu M.; 05.09.2013
comment
@Matthieu: Ваша версия избегает выделения кучи только за счет того, что она менее универсальна, например, ограничение размера. Большинство реализаций std::function содержат небольшой буфер объектов, который они могут использовать, если вы используете небольшой функтор. - person Puppy; 05.09.2013
comment
@MatthieuM. Реализации std::function выполняют небольшую оптимизацию буфера, которая аналогична этой, за исключением резервного варианта для поддержки всех сценариев (кучи). (Почему бы и нет? Если есть очевидная работающая оптимизация, почему stdlib ее пропустит?) - person R. Martinho Fernandes; 05.09.2013
comment
Некоторый наивный бенчмаркинг. raw func и raw memfunc просто вызывают функции в обычном режиме. don_delegate — это моя реализация FastDelegate Дона с shared_ptr<void> вместо aligned_storage по причинам, объясненным @user1095108. fastfunc2 - это реализация @Matthieu M. выше. fastfunc2 - это вышеприведенная реализация @Matthieu M. с использованием shared_ptr<void> вместо aligned_storage. Эталонный код - person Vittorio Romeo; 05.09.2013
comment
Ваши цифры не имеют значения, так как они не сравнивают яблоки и апельсины. Ни fastdelegates, ни ваши усилия, ни усилия Матье не являются такими общими, как std::function. - person Puppy; 05.09.2013
comment
@DeadMG: что может std::function сделать, чего не может моя реализация fastdelegate Дона? - person Vittorio Romeo; 05.09.2013
comment
@VittorioRomeo: Э-э, а как насчет захвата лямбда-выражений в Store - именно поэтому вы задаете вопрос в первую очередь? - person Puppy; 05.09.2013
comment
@DeadMG: тесты, которые я опубликовал, показывают сохраненные лямбда-выражения. Сначала я использовал std::aligned_storage, как предложил этот ответ, а затем попробовал std::shared_ptr<void>, что быстрее. Осталось ли что-нибудь реализовать, что может сделать std::function? - person Vittorio Romeo; 05.09.2013
comment
@VittorioRomeo Есть одна вещь, которую ты все еще можешь сделать. Изучите использование распределителей стека и распределителей, выделяющих из статических массивов. Это точно побьет std::shared_ptr. Boost.Pool приходит на ум. - person user1095108; 05.09.2013
comment
@MatthieuM. : ваша реализация FastFunc такая же быстрая, как FastDelegate Дона (но намного чище) во всех случаях, кроме одного: глобальных функций. Я потратил последний час, пытаясь каким-то образом связать в нем глобальные функции, но безуспешно. Любая идея, как я могу обрабатывать глобальные функции отдельно, чтобы вызов их через ваш FastFunc был таким же быстрым, как их обычный вызов? - person Vittorio Romeo; 05.09.2013
comment
@VittorioRomeo: я просматривал ваш тестовый код и заметил, что тела циклов не имеют побочных эффектов ... таким образом, они потенциально могут быть полностью оптимизированы (что объясняет случаи 0 ms). Что касается победы над FastDelegate Дона в глобальных функциях... понятия не имею, как он это делает (не слишком глубоко вникал в его код). Мой батут (функция apply) поверх алиасинга может мешать оптимизации. Я боюсь, что это нужно будет тщательно изучить (просмотреть IR компилятора и посмотреть, где оптимизатор задыхается), и у меня сейчас нет времени: x - person Matthieu M.; 05.09.2013
comment
@MatthieuM. попытался добавить простые побочные эффекты (и обязательно отследить их). Это все еще 0 ms. Я предполагаю, что эти необработанные функции полностью встроены компилятором. - person Vittorio Romeo; 05.09.2013
comment
@VittorioRomeo: Это тоже мое предположение, я полагаю, что волшебство, происходящее в моей быстрой и грязной реализации, слишком умно, чтобы компиляторы могли его увидеть :( - person Matthieu M.; 06.09.2013

Вы не можете.

Вот в чем дело. Fastdelegates работает только в очень немногих, очень специфических обстоятельствах. Вот что делает его быстрее. Вы не превзойдете своего разработчика стандартной библиотеки в реализации std::function.

person Puppy    schedule 05.09.2013

Я принял решение поместить лямбда-функцию в качестве указателя только в FastDelegate (он больше ничего не хранит), используя каторжный труд и пару других потоков, таких как: Получить тип лямбда-параметра

вот:

namespace details{

    template<class FPtr> struct function_traits;
    template<class RT, class CT                                                                                        >struct function_traits<RT (CT::*)(                                      )     >{ typedef RT Result;                                                                                                                                                                  typedef RT (CT::*Signature)(                                      );};
    template<class RT, class CT                                                                                        >struct function_traits<RT (CT::*)(                                      )const>{ typedef RT Result;                                                                                                                                                                  typedef RT (CT::*Signature)(                                      );};
    template<class RT                                                                                                  >struct function_traits<RT        (                                      )     >{ typedef RT Result;                                                                                                                                                                  typedef RT       Signature (                                      );};
    template<class RT, class CT, class P1T                                                                             >struct function_traits<RT (CT::*)(P1T                                   )     >{ typedef RT Result;  typedef P1T Param1;                                                                                                                                             typedef RT (CT::*Signature)(P1T                                   );};
    template<class RT, class CT, class P1T                                                                             >struct function_traits<RT (CT::*)(P1T                                   )const>{ typedef RT Result;  typedef P1T Param1;                                                                                                                                             typedef RT (CT::*Signature)(P1T                                   );};
    template<class RT          , class P1T                                                                             >struct function_traits<RT        (P1T                                   )     >{ typedef RT Result;  typedef P1T Param1;                                                                                                                                             typedef RT       Signature (P1T                                   );};
    template<class RT, class CT, class P1T, class P2T                                                                  >struct function_traits<RT (CT::*)(P1T, P2T                              )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2;                                                                                                                         typedef RT (CT::*Signature)(P1T, P2T                              );};
    template<class RT, class CT, class P1T, class P2T                                                                  >struct function_traits<RT (CT::*)(P1T, P2T                              )const>{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2;                                                                                                                         typedef RT (CT::*Signature)(P1T, P2T                              );};
    template<class RT          , class P1T, class P2T                                                                  >struct function_traits<RT        (P1T, P2T                              )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2;                                                                                                                         typedef RT       Signature (P1T, P2T                              );};
    template<class RT, class CT, class P1T, class P2T, class P3T                                                       >struct function_traits<RT (CT::*)(P1T, P2T, P3T                         )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3;                                                                                                     typedef RT (CT::*Signature)(P1T, P2T, P3T                         );};
    template<class RT, class CT, class P1T, class P2T, class P3T                                                       >struct function_traits<RT (CT::*)(P1T, P2T, P3T                         )const>{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3;                                                                                                     typedef RT (CT::*Signature)(P1T, P2T, P3T                         );};
    template<class RT          , class P1T, class P2T, class P3T                                                       >struct function_traits<RT        (P1T, P2T, P3T                         )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3;                                                                                                     typedef RT       Signature (P1T, P2T, P3T                         );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T                                            >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T                    )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4;                                                                                 typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T                    );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T                                            >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T                    )const>{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4;                                                                                 typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T                    );};
    template<class RT          , class P1T, class P2T, class P3T, class P4T                                            >struct function_traits<RT        (P1T, P2T, P3T, P4T                    )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4;                                                                                 typedef RT       Signature (P1T, P2T, P3T, P4T                    );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T                                 >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T               )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5;                                                             typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T               );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T                                 >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T               )const>{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5;                                                             typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T               );};
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T                                 >struct function_traits<RT        (P1T, P2T, P3T, P4T, P5T               )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5;                                                             typedef RT       Signature (P1T, P2T, P3T, P4T, P5T               );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T                      >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T          )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6;                                         typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T, P6T          );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T                      >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T          )const>{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6;                                         typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T, P6T          );};
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T, class P6T                      >struct function_traits<RT        (P1T, P2T, P3T, P4T, P5T, P6T          )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6;                                         typedef RT       Signature (P1T, P2T, P3T, P4T, P5T, P6T          );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T           >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T     )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6; typedef P7T Param7;                     typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T, P6T, P7T     );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T           >struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T     )const>{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6; typedef P7T Param7;                     typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T, P6T, P7T     );};
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T           >struct function_traits<RT        (P1T, P2T, P3T, P4T, P5T, P6T, P7T     )     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6; typedef P7T Param7;                     typedef RT       Signature (P1T, P2T, P3T, P4T, P5T, P6T, P7T     );};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T, class P8T>struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T)     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6; typedef P7T Param7; typedef P8T Param8; typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T);};
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T, class P8T>struct function_traits<RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T)const>{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6; typedef P7T Param7; typedef P8T Param8; typedef RT (CT::*Signature)(P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T);};
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T, class P8T>struct function_traits<RT        (P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T)     >{ typedef RT Result;  typedef P1T Param1; typedef P2T Param2; typedef P3T Param3; typedef P4T Param4; typedef P5T Param5; typedef P6T Param6; typedef P7T Param7; typedef P8T Param8; typedef RT       Signature (P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T);};

    template<class T>
    typename function_traits<T>::Signature* bar_helper(T);

    template<class F>
    class FuncTraitsOf{
    public:
        typedef decltype(bar_helper(&F::operator())) fptr;
        typedef typename std::remove_pointer<fptr>::type Signature;     //Signature =   bool __cdecl(int,float)
        typedef typename function_traits< Signature > R;                //R         =   struct function_traits<bool __cdecl(int,float)>
    };

    template< class FuncTraits>class FDSel;
    template<class RT, class CT                                                                                        > struct FDSel< function_traits< RT (CT::*)(                                      )      > >{ typedef fastdelegate::FastDelegate0<                                       RT> R; };
    template<class RT, class CT                                                                                        > struct FDSel< function_traits< RT (CT::*)(                                      )const > >{ typedef fastdelegate::FastDelegate0<                                       RT> R; };
    template<class RT                                                                                                  > struct FDSel< function_traits< RT        (                                      )      > >{ typedef fastdelegate::FastDelegate0<                                       RT> R; };
    template<class RT, class CT, class P1T                                                                             > struct FDSel< function_traits< RT (CT::*)(P1T                                   )      > >{ typedef fastdelegate::FastDelegate1<P1T                                   ,RT> R; };
    template<class RT, class CT, class P1T                                                                             > struct FDSel< function_traits< RT (CT::*)(P1T                                   )const > >{ typedef fastdelegate::FastDelegate1<P1T                                   ,RT> R; };
    template<class RT          , class P1T                                                                             > struct FDSel< function_traits< RT        (P1T                                   )      > >{ typedef fastdelegate::FastDelegate1<P1T                                   ,RT> R; };
    template<class RT, class CT, class P1T, class P2T                                                                  > struct FDSel< function_traits< RT (CT::*)(P1T, P2T                              )      > >{ typedef fastdelegate::FastDelegate2<P1T, P2T                              ,RT> R; };
    template<class RT, class CT, class P1T, class P2T                                                                  > struct FDSel< function_traits< RT (CT::*)(P1T, P2T                              )const > >{ typedef fastdelegate::FastDelegate2<P1T, P2T                              ,RT> R; };
    template<class RT          , class P1T, class P2T                                                                  > struct FDSel< function_traits< RT        (P1T, P2T                              )      > >{ typedef fastdelegate::FastDelegate2<P1T, P2T                              ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T                                                       > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T                         )      > >{ typedef fastdelegate::FastDelegate3<P1T, P2T, P3T                         ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T                                                       > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T                         )const > >{ typedef fastdelegate::FastDelegate3<P1T, P2T, P3T                         ,RT> R; };
    template<class RT          , class P1T, class P2T, class P3T                                                       > struct FDSel< function_traits< RT        (P1T, P2T, P3T                         )      > >{ typedef fastdelegate::FastDelegate3<P1T, P2T, P3T                         ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T                                            > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T                    )      > >{ typedef fastdelegate::FastDelegate4<P1T, P2T, P3T, P4T                    ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T                                            > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T                    )const > >{ typedef fastdelegate::FastDelegate4<P1T, P2T, P3T, P4T                    ,RT> R; };
    template<class RT          , class P1T, class P2T, class P3T, class P4T                                            > struct FDSel< function_traits< RT        (P1T, P2T, P3T, P4T                    )      > >{ typedef fastdelegate::FastDelegate4<P1T, P2T, P3T, P4T                    ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T                                 > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T               )      > >{ typedef fastdelegate::FastDelegate5<P1T, P2T, P3T, P4T, P5T               ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T                                 > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T               )const > >{ typedef fastdelegate::FastDelegate5<P1T, P2T, P3T, P4T, P5T               ,RT> R; };
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T                                 > struct FDSel< function_traits< RT        (P1T, P2T, P3T, P4T, P5T               )      > >{ typedef fastdelegate::FastDelegate5<P1T, P2T, P3T, P4T, P5T               ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T                      > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T          )      > >{ typedef fastdelegate::FastDelegate6<P1T, P2T, P3T, P4T, P5T, P6T          ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T                      > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T          )const > >{ typedef fastdelegate::FastDelegate6<P1T, P2T, P3T, P4T, P5T, P6T          ,RT> R; };
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T, class P6T                      > struct FDSel< function_traits< RT        (P1T, P2T, P3T, P4T, P5T, P6T          )      > >{ typedef fastdelegate::FastDelegate6<P1T, P2T, P3T, P4T, P5T, P6T          ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T           > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T     )      > >{ typedef fastdelegate::FastDelegate7<P1T, P2T, P3T, P4T, P5T, P6T, P7T     ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T           > struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T     )const > >{ typedef fastdelegate::FastDelegate7<P1T, P2T, P3T, P4T, P5T, P6T, P7T     ,RT> R; };
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T           > struct FDSel< function_traits< RT        (P1T, P2T, P3T, P4T, P5T, P6T, P7T     )      > >{ typedef fastdelegate::FastDelegate7<P1T, P2T, P3T, P4T, P5T, P6T, P7T     ,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T, class P8T> struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T)      > >{ typedef fastdelegate::FastDelegate8<P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T,RT> R; };
    template<class RT, class CT, class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T, class P8T> struct FDSel< function_traits< RT (CT::*)(P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T)const > >{ typedef fastdelegate::FastDelegate8<P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T,RT> R; };
    template<class RT          , class P1T, class P2T, class P3T, class P4T, class P5T, class P6T, class P7T, class P8T> struct FDSel< function_traits< RT        (P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T)      > >{ typedef fastdelegate::FastDelegate8<P1T, P2T, P3T, P4T, P5T, P6T, P7T, P8T,RT> R; };


}


template<class F>
typename details::FDSel< typename details::FuncTraitsOf<F>::R >::R MakeDelegate(F& f){
    return fastdelegate::MakeDelegate(&f, &F::operator());
}

Скопируйте/вставьте это в свой файл FastDelegate.h.

НЕ используйте его так:

home.visit(fastdelegate::MakeDelegate([&](const Room& a){ /* ... */ }));

Вместо этого сделайте следующее:

auto d = [&](const Room& a){ /* ... */ };
home.visit(fastdelegate::MakeDelegate(d));

Дайте мне знать, если я что-то пропустил.

person StephanieRct    schedule 25.02.2015

Разница между «тривиальными» и общими лямбда-функциями заключается в том, что если она не принадлежит к первому классу (без захватов), она является объектом функции.

Если вы скопируете объект (лямбда), и он содержит ссылки на временные объекты или ссылки на выделенные в стеке объекты, которые будут освобождены до того, как ваш FastDelegate будет уничтожен, у вас будет оборванная ссылка, отсюда и сбой.

Попробуйте захватить по копии, а не по ссылке

person Stefano Falasca    schedule 05.09.2013
comment
Ваше объяснение правильное, но дело в более быстром добавлении std::function без изменения существующего лямбда-кода. std::function работает в примере, где лямбда захватывает bool по ссылке, но медленно. Моя цель — использовать концепции, лежащие в основе работы Дона Клагстона, для создания более быстрой версии std::function. - person Vittorio Romeo; 05.09.2013
comment
@Vittorio: Вы не будете. Быстрые делегаты действительно работают только для небольшого подмножества сценариев, где может работать std::function. Вот почему это быстрее. Маловероятно, что вы превзойдете своего разработчика стандартной библиотеки. - person Puppy; 05.09.2013
comment
@DeadMG Да, но иногда вам может не нравиться интерфейс, и тогда вы делаете что-то свое. Например, мне не нравится использовать std::bind или лямбда-объекты с std::function для вызова функций-членов. - person user1095108; 05.09.2013
comment
@ user1095108: Если он не собирается сильно ограничивать свой интерфейс, он все еще создает класс с теми же ограничениями, что и std::function, что почти наверняка приведет к практически тем же компромиссам реализации и, следовательно, в основном к той же производительности. - person Puppy; 05.09.2013
comment
@VittorioRomeo когда-нибудь спрашивал себя, почему люди, реализующие std::function, не использовали концепции, лежащие в основе работы Дона Клагстона, для создания более быстрой версии std::function? - person R. Martinho Fernandes; 05.09.2013
comment
Или, другими словами: если вам удастся получить более быструю встраиваемую замену для std::function, не могли бы вы отправить патч разработчикам стандартной библиотеки, чтобы мы все могли получить вкусности? - person R. Martinho Fernandes; 05.09.2013
comment
@R.MartinhoFernandes: это было бы здорово. Я сомневаюсь, что это когда-либо произойдет (а если и произойдет, я сомневаюсь, что я буду тем, кто создаст более быструю замену для std::function). Но мне все равно нравится экспериментировать с ним - person Vittorio Romeo; 05.09.2013
comment
@DeadMG Я думаю, что эксперименты с ОП уместны; он ищет производительность, вескую причину для переключения. И std::function (через повышение) родом из C++03 дней. Может быть лучший интерфейс с С++ 11. - person user1095108; 05.09.2013
comment
@DeadMG: я нахожу абсурдным то, что привязка глобальной необработанной функции к std::functions имеет заметные накладные расходы. Например, использование быстрого делегата Дона полностью встраивает вызов глобальных необработанных функций. Я могу только думать, что это дефект текущих реализаций std::function. - person Vittorio Romeo; 05.09.2013
comment
@VittorioRomeo Это смешно. Как будто делегат Дона каким-то образом ускорил работу вашего go, если вызывать его через делегат :) - person user1095108; 06.09.2013
comment
@ user1095108: нет, я предполагаю, что, поскольку необработанная функция вызывается перед чем-либо еще, происходит некоторое кэширование или что-то в этом роде. Переупорядочивание тестов инвертирует время (dondelegate переходит к 20ms, а rawfunc переходит к 8 ms). - person Vittorio Romeo; 06.09.2013
comment
@VittorioRomeo заставляет вас сомневаться в своей скамейке запасных. - person user1095108; 06.09.2013