Я пытаюсь создать реализацию 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 ++ Дона Клагстона работать с лямбдами, которые фиксируют состояние, сохраняя при этом удивительную производительность fastdelegates?
Вы хорошо поставили диагноз: вам нужно сохранить состояние.
Поскольку лямбда является временным объектом, вам действительно разрешено перемещаться с него (обычно), который следует отдавать предпочтение копии, если это возможно (поскольку перемещение является более общим, чем копирование).
Теперь все, что вам нужно сделать, это зарезервировать для него некоторое хранилище, и если для этого требуется динамическое распределение, вы действительно можете получить снижение производительности. С другой стороны, объект должен иметь фиксированный отпечаток, так?
Одним из возможных решений является предложение настраиваемой (но ограниченной) емкости хранилища:
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;
}
Ты не можешь
Вот вещь Fastdelegates работает только для очень немногих, очень специфических обстоятельств. Вот что делает это быстрее. Вы не побьете своего стандартного разработчика библиотеки для реализации std::function
,
Я решил, что лямбда-функция может быть указана в виде указателя только в 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));
Дайте мне знать, если я что-то пропустил.
Разница между «тривиальными» и общими лямбда-функциями заключается в том, что если она не принадлежит первому классу (без захватов), то это функциональный объект.
Если вы копируете объект (лямбда), и он содержит ссылки на временные объекты или ссылки на выделенные объекты стека, которые будут освобождены до того, как ваш FastDelegate будет уничтожен, у вас будет свисающая ссылка, отсюда и сбой.
Попробуйте захватить по копии, а не по ссылке