Frage

I'm trying to create a C++11 implementation of Don Clugston's Member Function Pointers and the Fastest Possible C++ Delegates, and make it work as a drop-in std::function replacement.

This is what I got so far.

I construct lambda FastDelegates like this:

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

Now, some tests:

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!

As you can see, when the lambda is "trivial" (no captures), copying it by value and taking its address works. But when the lambda stores some kind of state (captures), I cannot just copy it by value into the FastFunc.

I tried getting the lambda by reference, but I cannot do that when it's a temporary like in the example.

I have to somehow store the lambda inside the FastFunc, but I don't want to use std::shared_ptr because it's slow (I tried a different fastdelegate implementation that used it, and its performance was comparable to std::function).

How can I make my implementation of Don Clugston's fastest possible C++ delegates work with lambdas that capture state, preserving the amazing performance of fastdelegates?

War es hilfreich?

Lösung

You have diagnosed the situation well: you need to store the state.

Since the lambda is a temporary object, you are actually allowed to move from it (normally) which should be preferred to a copy if possible (because move is more general than copy).

Now, all you need to do is to reserve some storage for it, and if this requires a dynamic allocation you might indeed get a performance degradation. On the other hand, an object need have a fixed foot-print, so ?

One possible solution is to offer a configurable (but limited) storage capacity:

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;

Now you can (using reinterpret_cast as necessary) store your lambda within storage provided its size fit (which can be detected using static_assert).

Finally managed to get a working example (had to restart from scratch because god is that fast delegate code verbose !!), you can see it in action here (and the code is below).

I have only scratch the surface, notably because it lacks copy and move operators. To do so properly those operations need be added to the handler following the same pattern than the two other operations.

Code:

#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;
}

Andere Tipps

You can't.

Here's the thing. Fastdelegates only works for a very few, very specific circumstances. That's what makes it faster. You won't beat your Standard library implementer for implementing std::function.

I've made a solution to fit a lambda function as pointer only into a FastDelegate (it does not store anything else) using hard labor and a couple of other thread such as: Get lambda parameter type

here it is:

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());
}

Copy/paste that into your FastDelegate.h file.

Do NOT use it like this:

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

Instead do this:

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

Let me know if I missed anything.

The difference between "trivial" and general lambda functions is that if it doesn't belong to the first class (no captures) it is a function object.

If you copy the object (lambda) and it contains references to temporary objects, or references to stack allocated objects that will be freed before your FastDelegate gets destroyed, you have a dangling reference, hence the crash.

Try capturing by copy, not by reference

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top