std::mutex m;
std::locked(m, [&]{
//modify shared state
});
template <typename Lockable, typename Func>
void locked(Lockable &lockable, Func func) {
lock_guard<Lockable> local_lock(lockable);
func();
}
template <typename ...LockableN, typename Func>
void locked(LockableN &...lockableN, Func func) { //leading variadic parameters not currently legal
std::lock(lockableN...);
try {func();}
catch(...) {std::unlock(lockableN...);} //std::unlock doesn't currently exist
std::unlock(lockableN...);
}
I want to be able to do this in C++1y:
std::mutex m;
std::locked(m, [&]{
//modify shared state
});
It also solves an aesthetic issue. With std::locked, I don't need to name the Lockable type every time I want to lock the object, where with lock_guard, I get to mention the type repeatedly.
Your function seems to cover almost the same needs. For the function name I would prefer std::synchronize. It would be great to do some performances comparisons between std::locked and the direct use of std::lock_guard.
#define synchronized(MUTEX) \ if (bool _stop_ = false) {} else \ for (std::lock_guard<decltype(MUTEX)> _lock_(MUTEX); !_stop_; _stop_ = true)
lock_guard<
std::mutex> _lock_(mtx); // _lock_
should be a unique identifier that doesn't conflicts with any
other identifier.
//... STATEMENT
}@frame
@keyword synchronized // declares a
new keyword
@grammar statement |= <synchronize-statement> // augment the current definition of a statement
@grammar synchronize-statement ::= synchronized ( <expr:expression> ) <stmt:statement> // defines the new statement
@identifier _lock_ // generates a unique
identifier usable only in this transformation
{
$(stmt)
}
That's an interesting idea. It's an example of the "Execute Around
Object" pattern, which is a nice counterpart to RAII.
#include <mutex>
template <typename Lockable, typename Function>
inline auto with_lock(Lockable &lockable, Function fn) -> decltype(fn())
{
std::lock_guard<Lockable> local_lock(lockable);
return fn();
}
//...
std::mutex m;
const bool result = with_lock(m, []() -> bool
{
// ...
return true;
});
const bool result = with_lock(m, []() -> bool
{
// ...
return true;
})();
Something like "std::locked" solves a problem I have seen many times. People intend to write "std::lock_guard<std::mutex> lock(m);", but instead write "std::lock_guard<std::mutex>(m);". The second construct locks and immediately unlocks the mutex, protecting nothing.
std::lock_guard<auto> lock(m); //template type deduced from constructor argument type
//or even better...
std::lock_guard lock(m); //template in disguise.
synchronized(mutex)
{
// ...
}
// equivalent to:
{
std::lock_guard<decltype(mutex)> __lock;
// ...
}
synchronized(mutex, custom_lock<std::mutex> x)
{
// ...
}
// equivalent to
{
custom_lock<std::mutex> x(mutex);
// ...
}
Il giorno 21/nov/2012, alle ore 23:51, Ben Craig <ben....@gmail.com> ha scritto:
> they will be much more likely to use lambdas for things like comparator functions.
Lambda just for comparators? That's silly. Important frameworks in C++ and other languages are already using lambdas (or their equivalent) in interfaces similar to one described in the OP. For example several iOS frameworks use these kind of idioms and I see a definite trend in this direction.
This syntax IS RAII. A specific form of it, I agree, where you don't care about the name of the variable, but still RAII.
> I currently know of two broad use cases with the unused temporary problem, locking, and tracing / logging. Are these issues severe enough to introduce a new syntax that would only really be used in those areas?
State savers and iostream sentries are other use cases.
I have a proposal for the library based approach, very similar to my initial post. I have attached the html version, and you can view an online version here:
https://docs.google.com/document/d/1hSMTw-N2PxapY0SykgfGAPY41HnTMNs7CHKEcptFdzA/edit
Further on the part
"The object f is permitted to return any value."
should be removed, because it has no additional value.
template <typename Mutex, typename Func>doesn't require detail::get_unique_lock_ref, because you could rewrite that as
auto with_lock(Mutex &, Func f) -> decltype(f(declval<unique_lock<Mutex>&>()))
where declval is already part of the library spec.
You should take your Func-Object by RValue Reference to prevent copying.
Folly gets it right: https://github.com/facebook/folly/blob/master/folly/Synchronized.h
Which part of the reasoning / information is inaccurate? Note that this is specifically rejecting a synchronized keyword, and not a Folly like Synchronized class / macro.
2012/11/28 Ben Craig <ben....@gmail.com>
I have a proposal for the library based approach, very similar to my initial post. I have attached the html version, and you can view an online version here:
https://docs.google.com/document/d/1hSMTw-N2PxapY0SykgfGAPY41HnTMNs7CHKEcptFdzA/edit
int q;
{
std::lock_guard<std::mutex> lk(m);
++global_count;
q =
global_count;
}
std::condition_variable cond_var;
bool
notified = false;
{
std::unique_lock<std::mutex> lk(m);
while
(!notified)
cond_var.wait(l);
}
std::mutex m;
int q;
{
auto && lk = std::make_lock_guard(m);
++global_count;
q =
global_count;
}
std::condition_variable cond_var;
bool
notified = false;
{
auto lk = std::make_unique_lock(m);
while
(!notified)
cond_var.wait(l);
}
std::mutex m;
int q;
auto&& ... = std::make_lock_guard(m) : {
++global_count;
q =
global_count;
}
Note the use of ... for the unnamed
variable and the colon ':' to introduce a variable in a statement.
bool
notified = false;
auto lk = std::make_unique_lock(m) : {
while
(!notified)
cond_var.wait(l);
}
Le 28/11/12 09:09, stackm...@hotmail.com a �crit :
> Folly gets it right:
> https://github.com/facebook/folly/blob/master/folly/Synchronized.h
>
How? Are you suggesting to define a macro?
--Vicente
I don't know if the motivating example is a good one to show the advantages of your library.
The problem with
std::lock_guard<std::mutex>(m);
is a general RAII problem, and if it is enough important, every RAII in the C++ standard should be need to be considered.
Yes, this macro would be 10 times better at least. Along with UNSYNCHRONIZED, TIMED_SYNCHRONIZED, etc.
Am Donnerstag, 29. November 2012 04:13:21 UTC+1 schrieb Ben Craig:
Yes, this macro would be 10 times better at least. Along with UNSYNCHRONIZED, TIMED_SYNCHRONIZED, etc.
A macro based implementation has similar problems to a keyword based approach in the amount of code that it breaks. Macros don't obey namespaces. If C++1y were to use Folly's approach, and used the same names as Folly, then it would almost certainly break code that uses Folly. If it uses different "good" names, it will probably break some other library. I think the only macro names it could use that wouldn't break code (at least according to the standard) would be names with leading underscores.
I will address Folly in the alternatives section, as it is still a very nice facility. Some use cases, like locking a vector, are very nice. Other use cases aren't really improved by the Folly approach though, like locking for the duration of every member function of a class. Folly's approach is also more invasive. Your lock and data declarations need to change. It is harder for locks that aren't packaged with SYNCHRONIZED to be usable by SYNCHRONIZED. You have to overload op-> in a very unusual way.
You don't have to tell me the problems of macros, I'm well aware of that. I'm also not saying that we need to introduce these macros. But with current language features it's better than your solution.
To convince users of the standard library to use your facility, you have to show use cases where it helps them write better oder simpler code. I personally have never seen the problem you mentioned happening, plus your facility makes my code more verbose. Why would i want to use it?Folly's macros however help me write simpler code, by just writing SYNCHRONIZED, instead of some lock_guard (which is much simpler to type), plus i don't have to create an extra mutex, it's packaged together with the object i want to lock. That is a very convenient solution I would definitely want to use as a user.
In my opinion you have either 2 options that would work:
- Put it into the standard library, using macros
- Bake it into the core language
Any other options are too verbose and don't have enough advantages for users.
That being said, how about this: add keywords like synchronized, and make them only available if the header <synchronized> is included. Allow code to use these keywords as identifiers as long as this header isn't included.
And if you want to make it a library feature: Prefix these macros by STD_