2

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?

Robin Green
  • 32,079
  • 16
  • 104
  • 187
mark
  • 89
  • 1
  • 6
  • In this line of code: `template< typename Fn, typeName ...Args >` is the 2nd parameter `typeName` a typo ? Also on this line of code: `bool checkRules( const int, const int, bool& std::vector< bool >& );` are you missing a comma between parameters? – Francis Cugler Nov 05 '18 at 22:53
  • Yes, Francis. When I typed in here I missed that comma. – mark Nov 06 '18 at 14:17
  • I thought you did but wanted to make sure, because something simple like that can produce completely different errors than the one you are describing. This is why it is best if at all possible to copy the code from your IDE directly to your stack post. – Francis Cugler Nov 07 '18 at 02:48

2 Answers2

3

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));
}
Jans
  • 11,064
  • 3
  • 37
  • 45
  • 2
    Wow excellent answer! I was missing the two main components of solving this myself and you beat me to it. I was missing the fact of using `std::reference_wrapper` due to `decay` along with `std::ref` which I might of eventually moved onto, but the biggest one was the use of `std::placeholders::_1...` in which I was not familiar with. I appreciate this kind of answer for I myself learned something new! IMHO I think you should get the accepted answer! – Francis Cugler Nov 06 '18 at 00:50
  • Thank you so much Jans! It definitely was a big step forward, but unfortunately still not quite there yet :-( I am now getting some error complaining about "no type named 'type' " as follows: functional:1665:61: error: no type named 'type' in 'class std::result_of&)>(TestChild*, std::_Placeholder<1>, std::_Placeholder<2>, std::_Placeholder<3>, std::_Placeholder<4>)>(int, int, bool, std::reference_wrapper >)>' typedef typename result_of<_Callable(_Args...)>::type result_type; Thank you! – mark Nov 06 '18 at 14:09
  • 1
    Thank you Francis! I am also going to take a close look at the std::placeholders::_1 ... myself, something that is new to me as well. – mark Nov 06 '18 at 14:11
  • @mark did you take a closer look to the code in the answer?, after these changes the error disappeared. – Jans Nov 06 '18 at 14:36
  • @Jans: Thank you so much Jans! Yes, adding the reference_wrapper made the errors go away, and now it compiles~ Your help is very much appreciated!! – mark Nov 06 '18 at 20:09
1
return checkBlock( toCheck, &testChild::checkRules, this, &results);

You're passing this in with your Args which doesn't match your parameters to your function, so there's an extra TestChild*& that doesn't belong.

return checkBlock( toCheck, &testChild::checkRules, ~~this~~, &results);

Just remove ~~this~~

Also, you should std::forward your Args as such:

                                        toCheck, std::forward<Args>(args) ... ) );
xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • Thank you Xaxxon! I tried with removing 'this' and putting std::forward to Args as well as Fn, still didn't work :-( – mark Nov 06 '18 at 14:13