Alternative to std::function for passing function as argument (callbacks, etc.)

前端 未结 3 1901
面向向阳花
面向向阳花 2020-12-29 06:14

I stumbled across this during my experiments with C++11. I find that it is an obvious solution, but I haven\'t been able to find any other examples of it in the wild, so I\'

相关标签:
3条回答
  • 2020-12-29 06:56

    You are using a raw pointer to function.

    Unlike std::function, this will not work with a lambda that captures, or with a result of std::bind, or with a general class type that implements operator().

    0 讨论(0)
  • 2020-12-29 07:04

    "Alternative to std::function for passing function as argument"

    One alternative would be a function pointer (including member function pointer). But std::function is just so much nicer (IMO).

    0 讨论(0)
  • 2020-12-29 07:14

    auto callback(std::future<int>) -> void is the declaration of a entity of type void(std::future<int>) called callback. When listed as an argument, the compiler adjusts this to be a pointer-to-function of type void(*)(std::future<int>).

    Your lambda is stateless, and as such can be implicitly converted to a function pointer.

    Once you add a non-trivial capture, your code will stop compiling:

    [argc](std::future<int> number) { 
       std::cout << argc << '\n';
    

    ...

    Now, ignoring your question content and looking at the title...

    There is a modest cost for a std::function because it is a value-type, not a view-type. As a value-type, it actually copies its argument.

    You can get around this by wrapping the calling object in a std::ref, but if you want to state "I won't keep this function object around longer than this call", you can write a function_view type as follows:

    template<class Sig>
    struct function_view;
    
    template<class R, class...Args>
    struct function_view<R(Args...)> {
      void* ptr = nullptr;
      R(*pf)(void*, Args...) = nullptr;
    
      template<class F>
      using pF = decltype(std::addressof( std::declval<F&>() ));
    
      template<class F>
      void bind_to( F& f ) {
        ptr = (void*)std::addressof(f);
        pf = [](void* ptr, Args... args)->R{
          return (*(pF<F>)ptr)(std::forward<Args>(args)...);
        };
      }
      // when binding to a function pointer
      // even a not identical one, check for
      // null.  In addition, we can remove a
      // layer of indirection and store the function
      // pointer directly in the `void*`.
      template<class R_in, class...Args_in>
      void bind_to( R_in(*f)(Args_in...) ) {
        using F = decltype(f);
        if (!f) return bind_to(nullptr);
        ptr = (void*)f;
        pf = [](void* ptr, Args... args)->R{
          return (F(ptr))(std::forward<Args>(args)...);
        };
      }
      // binding to nothing:
      void bind_to( std::nullptr_t ) {
        ptr = nullptr;
        pf = nullptr;
      }       
      explicit operator bool()const{return pf;}
    
      function_view()=default;
      function_view(function_view const&)=default;
      function_view& operator=(function_view const&)=default;
    
      template<class F,
        std::enable_if_t< !std::is_same<function_view, std::decay_t<F>>{}, int > =0,
        std::enable_if_t< std::is_convertible< std::result_of_t< F&(Args...) >, R >{}, int> = 0
      >
      function_view( F&& f ) {
        bind_to(f); // not forward
      }
    
      function_view( std::nullptr_t ) {}
    
      R operator()(Args...args) const {
          return pf(ptr, std::forward<Args>(args)...);
      }
    };
    

    live example.

    This is also useful in that it is a strictly simpler kind of type erasure than std::function, so it could be educational to go over it.

    0 讨论(0)
提交回复
热议问题