Search code examples
multithreadingc++11stdthreadstdasyncstd-future

Using c++11's std::async inside an abstract base class


Why doesn't making threads like this work inside of an abstract base class? I'm trying to abstract away all of the multithreading details for users who derive from this base class. I don't understand why it says "no type named 'type'" when I clearly write that callbackSquare returns type int.

#include <iostream>
#include <future>
#include <vector>

class ABC{
public:
    std::vector<std::future<int> > m_results;
    ABC(){};
    ~ABC(){};
    virtual int callbackSquare(int& a) = 0;
    void doStuffWithCallBack();
};

void ABC::doStuffWithCallBack(){
    for(int i = 0; i < 10; ++i)
        m_results.push_back(std::async(&ABC::callbackSquare, this, i));

    for(int j = 0; j < 10; ++j)
        std::cout << m_results[j].get() << "\n";
}

class Derived : public ABC {
    Derived() : ABC() {};
    ~Derived(){};
    int callbackSquare(int& a) {return a * a;};
};

int main(int argc, char **argv)
{

    std::cout << "testing\n";

    return 0;
}

The strange errors I'm getting are:

/usr/include/c++/5/future:1709:67:   required from 'std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = int (ABC::*)(int&); _Args = {ABC*, int&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = int]'
/usr/include/c++/5/future:1725:19:   required from 'std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(_Fn&&, _Args&& ...) [with _Fn = int (ABC::*)(int&); _Args = {ABC*, int&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = int]'
/home/taylor/Documents/ssmworkspace/callbacktest/main.cpp:16:69:   required from here
/usr/include/c++/5/functional:1505:61: error: no type named 'type' in 'class std::result_of<std::_Mem_fn<int (ABC::*)(int&)>(ABC*, int)>'
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/5/functional:1526:9: error: no type named 'type' in 'class std::result_of<std::_Mem_fn<int (ABC::*)(int&)>(ABC*, int)>'
         _M_invoke(_Index_tuple<_Indices...>)

Solution

  • Your problem can be reproduced with any function that accepts a reference:

    #include <future>
    
    int f(int& a)
    {
        return a * a;
    }
    
    int main()
    {
        int i = 42;
        auto r = std::async(f, i);
    }
    

    Accepting a reference in your code is risky since the variable will be modified by the loop iteration, creating a data race because the called function also accesses the variable.

    Change the function to accept the input parameter by value, or call std::async by passing std::ref(i) or std::cref(i) (in case the function accepts a const reference) if you acknowledge the risk.