Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2004-07-08 02:12:07


Michael Glassford wrote:
> Eric Niebler wrote:

>>
>> How about:
>>
>> if( ScopedLock l = try_lock( m ) )
>> {
>> }
>>
>> where try_lock is function that returns a simple wrapper class:
>>
>> template< Mutex > struct try_locker
>> {
>> Mutex & m;
>> ...
>> };
>>
>> template< Mutex > try_locker< Mutex > try_lock( Mutex & m )
>> {
>> return try_locker< Mutex >( m );
>> };
>>
>> and ScopedLock is some lockable type which has a constructor that
>> accepts a try_locker. (It would also need a bool-ish conversion to
>> allow the lock to be declared in the "if" statement, but that's not
>> relevant to this discussion.)
>
>
> Interesting idea. I suppose you could even do this:
>
> if (ScopedLock l(m, locker()))
> {
> }
>
> where locker() is a function object that defines
>
> operator()(LockType& l)
>
> which the lock calls in the constructor, passing *this. You could have
> locker(), try_locker(), timed_locker(t), etc.
>
>

Not quite. In order to declare and initialize a variable in an "if"
statement, you need to use the "if( type var = fun() )" form, where the
"=" is necessary.

I realize that the code I posted before is incompatible with the
requirement that ScopedLock be non-copyable. Below is an interesting
variation which works around the problem. It defines a single type
"lock" which is non-copyable, yet it can be returned by value from a
function. (This may or may not be desirable for a lock class, but it's
interesting.)

   struct lock;
   lock try_lock();

   struct lock_ref
   {
     lock_ref(lock & ref)
       : ref_(ref)
     {
     }
     lock & ref_;
   };

   struct lock
   {
     lock(lock_ref ref){}
     operator lock_ref()
     {
       // ... move the lock ...
       return lock_ref(*this);
     }
     operator bool() const
     {
       // TODO return true iff we're holding the lock
       return true;
     }

   private:
     friend lock try_lock();
     lock(/*params*/){} // a try-lock c'tor

     // make this type non-copyable, non-assignable
     lock(lock &);
     lock & operator=(lock &);
   };

   inline lock try_lock(/*params*/)
   {
     // call a special try-lock c'tor on lock
     return lock(/*params*/);
   }

   int main()
   {
     if( lock l = try_lock(/*params*/) )
     {}
   }

The idea is to have just one lock class (leaving aside read/write locks)
and a collection of factory functions like try_lock(), timed_lock(),
defered_lock() etc. Those functions create a lock object by calling the
appropriate (private) constructor and returning the new lock object.
(Some fancy auto_ptr-like move shenanigans are needed to make the lock
return-able but not copy-able.)

I'm not really advocating this design. I'm not close enough to this
problem to have a strong preference. Just offering it as an alternative.

-- 
Eric Niebler
Boost Consulting
www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk