I want to accept any lambda, so that it can perform some operation safely under the lock_guard
and then return anything, but using as shown below throws an error:
#include <iostream>
#include <functional>
#include <mutex>
#include <memory>
class Test {
public:
template<typename Ret>
Ret DoLocked(std::function<Ret(Test&)> func) {
std::lock_guard<std::mutex> lock(*mtx);
return func(*this);
}
private:
std::unique_ptr<std::mutex> mtx = std::make_unique<std::mutex>();
};
int main() {
Test a;
a.DoLocked([](Test &s) {
std::cout << "in do locked" << std::endl;
});
return 0;
}
[build] /some-path/test.cc:21:6: error: no matching function for call to ‘Test::DoLocked(main()::<lambda(Test&)>)’
[build] 21 | });
[build] | ^
[build] /some-path/test.cc:9:13: note: candidate: ‘template<class Ret> Ret Test::DoLocked(std::function<Ret(Test&)>)’
[build] 9 | Ret DoLocked(std::function<Ret(Test&)> func) {
[build] | ^~~~~~~~
[build] /some-path/test.cc:9:13: note: template argument deduction/substitution failed:
[build] /some-path/test.cc:21:6: note: ‘main()::<lambda(Test&)>’ is not derived from ‘std::function<Ret(Test&)>’
[build] 21 | });```
CodePudding user response:
This is easily solved by getting rid of std::function
and making the function parameter a template parameter. That would look like
template<typename Func>
auto DoLocked(Func func) -> decltype(func(*this)) {
std::lock_guard<std::mutex> lock(*mtx);
return func(*this);
}
The reason it doesn't work with the std::function
is that a lambda expression does not create a std::function
. It creates a object of an unnamed class type that has an operator()
defined with the body of the lambda expression. Because of this, template deduction fails as it is expecting a std::function
but that is not what is being provided.
CodePudding user response:
I want to accept any lambda, so that it can perform some operation safely under the lock_guard
This is completely wrong assumption which will lead to deadlock.
Locks are to protect consistency of some specific data. Those data usually are fields of some single class.
Now critical section should protect this data and must not invoke any arbitrary code. Why? Since arbitrary code can contain other critical sections. This means that you do not have control on order of locks which are acquired. As a result small unrelated change to code will lead to hard to remove dead lock.