Search code examples
c++variadic-templatesstdasync

C++ errors with Variadic template


I have the following code using variadic templates to call std::async,

struct TestParent
{
    template< typename Fn, typeName ...Args >
    bool checkBlock( bool& toCheck,
                     Fn&& fn, Args&& ... args )
    {
        int startIndx = 0;
        int endIndx = 10;
        std::future< bool > tk( std::async( std::launch, fn, this,
                                            startIndx, endIndx, 
                                            toCheck, args ... ) );
        return tk.get();
     }
}

struct TestChild: public TestParent
{
    bool checkRules( const int, const int, bool& std::vector< bool >& );
    bool check();
}

bool TestChild::checkRules( const int startIndx, const int endIndx,
                            bool& toCheck,
                            std::vector< bool >& results )
{
    toCheck = true;
    for ( int indx = startIndx; indx < endIndx; ++ indx )
    {
        bool checkedOk;
        ... do something checking.
        results.push_back( checkedOk );
    }

    return true;
 }

bool TestChild::check()
{
    bool toCheck;
    std::vector< bool > results;
    return checkBlock( toCheck, &testChild::checkRules, this, &results);
}

But I'm getting the following compilation error message:

no matching function for call to 'async(std::launch, bool (TestChild::&)(int, int, bool&, std::vector&), TestParent, int&, int&, bool&, TestChild*&, std::vector*&)' startInx, endInx, nothingToCheck, args ... ) );

I think it might have something to do with the fact that I'm passing additional parameters along with a parameter pack. Anyone has any ideas what's wrong with this, and what I should do to get this to work?


Solution

  • These are the two main issues in the code:

    (1) std::async decay all the passed argument before forwarding them to the provided function, this mean that the references parameter in checkRules differ from the type async is trying to use when invoking the function, you need to do the following change:

    template< typename Fn, typename ...Args >
    bool checkBlock( std::reference_wrapper<bool> const& toCheck,
                    Fn&& fn, Args&& ... args )
    {
        int startIndx = 0;
        int endIndx = 10;
        std::future< bool > tk(std::async(std::launch::async,
                                           std::forward<Fn>(fn),
                                           startIndx, endIndx,
                                           toCheck,
                                           std::forward<Args>(args) ... ) );
        return tk.get();
    }
    

    (2) You're passing this as argument to checkBlock which eventually will end up as argument of checkRules (by async call), but the member function doesn't accept TestChild* to match this. Since you're using a pointer to member function to async you need to use std::bind to bind the this arguments and use std::wrap for the arguments you want to change:

    #include <functional>
    using namespace std::placeholders;
    
    bool TestChild::check()
    {
        bool toCheck;
        std::vector< bool > results;
        return checkBlock( std::ref(toCheck), std::bind(&TestChild::checkRules, this, _1, _2, _3, _4), std::ref(results));
    }