Search code examples
c++c++11stdvariadic-templates

C++ 11 bound std::function vs storing tuple and unpacking


First off, I am still relatively new to C++11, so if I am missing anything, pardon my oversight. So what I am trying to do is basically have caller pass in a function and arbitrary # of arguments for that function, store that off and then call it asynchronously later on. And it seems there are 2 main options for this:

  • use std::bind to bind a std::function to its arguments (which is obtained using variadic template) and then invoke it later
  • convert the parameter pack into a tuple, store that and std::function, then unpack tuple into multiple args again and invoke function using that

Question is, is one way better than the other? Are there pro/cons/performance benefits of one over the other?

Thanks!

EDIT: as requested, here is a clarification, the first case is more early binding where I bind args to function as soon as caller passes them on and I store the bound func to be called later. the 2nd case is where I store func and args separately and invoke function with the args later on when it has to be called. So question is which is better performance/codesize/style/etc?


Solution

  • Accept a std::function<...> with the appropriate signature, store it to callback later. Let the caller decide how they prefer to create/populate the parameter. E.g.,

    #include <functional>
    #include <iostream>
    
    std::function<int(int)> stored_f;
    
    void set_callback(std::function<int(int)> f) {
        stored_f = std::move(f);
    }
    
    void run_the_callback(int value) {
        std::cout << stored_f(value) << '\n';
    }
    
    int f(int i) {
        return i + 1;
    }
    
    int g(int a, int b) {
        return a + b;
    }
    
    int main() {
        // Plain old function pointer
        set_callback(f);
        run_the_callback(1);
        // Use std::bind
        set_callback(std::bind(g, 2, std::placeholders::_1));
        run_the_callback(2);
        // Use a lambda
        set_callback([](int i){ return f(i) * g(i, i);});
        run_the_callback(3);
    }
    

    Best performance - if you don't absolutely require type erasure of the callback - would be to parameterize your code on functor type. E.g.:

    #include <functional>
    #include <iostream>
    
    template <typename Functor>
    void do_stuff_and_callback_sometimes(Functor f) {
        std::cout << f(1) << '\n';
        // do some stuff, then
        std::cout << f(2) << '\n';
        // more work, and finally
        std::cout << f(3) << "\n\n";
    }
    
    int f(int i) {
        return i + 1;
    }
    
    int g(int a, int b) {
        return a + b;
    }
    
    int main() {
        // Plain old function pointer
        do_stuff_and_callback_sometimes(f);
        // Use std::bind
        do_stuff_and_callback_sometimes(std::bind(g, 2, std::placeholders::_1));
        // Use a lambda
        do_stuff_and_callback_sometimes([](int i){ return f(i) * g(i, i);});
    }
    

    Avoiding type erasure is impossible in some situations, and in others will require you to jump through hoops. Whether or not it's worthwhile to do so is situational.