I have the following function:
void MyClass::myFunc(cv::OutputArray dst) const {
cv::Mat result;
...
dst.assign(result);
}
If I run it like this:
cv::Mat result;
myFunc(result);
otherFunc(result);
It works fine and otherFunc
recieves result
modified by myFunc
.
But if I use std::async
like this:
cv::Mat result;
auto resultFuture = std::async(&MyClass::myFunc, this, result);
resultFuture.wait();
otherFunc(result);
otherFunc
receives empty result
. What am I doing wrong?
CodePudding user response:
The root cause is that passing arguments by reference (&) to a function to run via std::async
is problematic. You can read about it here: Passing arguments to std::async by reference fails (in your case there is no a compilation error, but the link explains the issue in general).
And in your case you use cv::OutputArray
which is defined in opencv as a refernce type:
typedef const _OutputArray& OutputArray;
I assume you wanted the reference semantics, since you expected your result
object to be updated by myFunc
.
The solution is to use std::ref
.
But since the result
object you pass is a cv::Mat
, it preferable and more straightforward that myFunc
will receive a cv::Mat&
.
I also managed to produce a solution using a cv::OutputArray
, but it requires an ugly cast (in addition to the std::ref
). It works fine on MSVC, but I am not sure it is will be generally valid.
Below is the code demostrating these 2 options. I recomend to use the 1st approach if you can. You can call otherFunc(result);
at the point where I print the dimensions of result
after it is initialized.
#include <opencv2/core/core.hpp>
#include <future>
#include <iostream>
// Test using a cv::Mat &:
class MyClass1
{
void myFunc(cv::Mat & dst) const
{
cv::Mat result(4, 3, CV_8UC1);
result = 1; // ... initialize some values
dst = result; // instead of using cv::OutputArray::assign
}
public:
void test()
{
cv::Mat result;
std::cout << "MyClass1: before: " << result.cols << " x " << result.rows << std::endl;
auto resultFuture = std::async(&MyClass1::myFunc, this, std::ref(result));
resultFuture.wait();
// Here result will be properly set.
std::cout << "MyClass1: after: " << result.cols << " x " << result.rows << std::endl;
}
};
// Test using a cv::OutputArray:
class MyClass2
{
void myFunc(cv::OutputArray dst) const
{
cv::Mat result(4, 3, CV_8UC1);
result = 1; // ... initialize some values
dst.assign(result);
}
public:
void test()
{
cv::Mat result;
std::cout << "MyClass2: before: " << result.cols << " x " << result.rows << std::endl;
auto resultFuture = std::async(&MyClass2::myFunc, this, std::ref(static_cast<cv::OutputArray>(result)));
resultFuture.wait();
// Here result will be properly set.
std::cout << "MyClass2: after: " << result.cols << " x " << result.rows << std::endl;
}
};
int main()
{
// Test receiving a cv::Mat&:
MyClass1 m1;
m1.test();
// Test receiving a cv::OutputArray:
MyClass2 m2;
m2.test();
return 0;
}