$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] Formal Review Request: TypeErasure
From: Hite, Christopher (Christopher.Hite_at_[hidden])
Date: 2012-06-22 12:03:04
Dave Abrahams wrote:
>>> > Consider this big monster object with this interface.  It's not 
>>> > movable/copyable (maybe it has its own threads).
>>> > I think you'd find something like this acceptable.
>>> >
>>> > 	struct monster : noncopyable{
>>> > 		typedef signal<void ()>::slot_type slot;
>>> >
>>> > 		connection subscribeA(slot);
>>> > 		connection subscribeB(slot);
>>> > 		connection subscribeC(slot);
>>> > 		...
>>> > 	};
>>> Would you prefer to put all of monster's guts in a shared_ptr-ed 
>>> pimple so monster can be copied?
> Only if your pet monster is immutable.  Otherwise, no.
I'm not sure what you mean by immutable.  It has deep state.  Users can change that state. However someone could say a shared_monster is immutable the pointer to its guts can't be changed.
>> This is an honest question.  I may be a couple steps behind on best 
>> practices of C++.  I'd probably give the user of monster an object he 
>> can't move/copy.
> Why not give him one he can move, so e.g. he can put it in a vector without getting into dynamic allocation?
Why not give him a unmovable object and let him put it in a smart pointer, deciding if he wants a singlton, single owner or shared instance?
If we start off with an unmovable object which does no dynamic allocation.  A movable verson probably forces it on the user.
> Yes.  Those are typically movable and non-copyable in the standard library (e.g. thread, mutex, ...).
Nevin Liber  points out
+> I don't believe that std::mutex is movable 
mutex isn't movable.  Which seems logical to me.  mutex::lock() isn't const either.
> > Here's the 0.1% case I tend to have, but maybe you think it's bad 
> > form.  If an object has members which are needed for synchronization 
> > with a private thread, it can't be moved.  Say I've got an class that 
> > has a private member mutex and thread it uses.  I can't safely just 
> > move the mutex.
> You have created a piece of shared, mutable state (the mutex).  To move it (a mutating operation), you'd need to at /least/ synchronize...
> which sorta "begs the question."
Yeah that's my point.  An object encapsulating a thread needs extra (questionable) complexity to support move.
> > A move constructor would have to
> >
> > * tell the worker thread to go park itself and wait for instructions
> >   from some temporary synchronization object
> > * wait until it's parked
> > * move the inards of my object including mutexes
> > * tell the thread to continue using the new location That seems hard
> >   and error prone.  So I'd never implement it unless there was a need.
> Right.  Such an object should be treated as const (and thus non-movable) whenever it is being accessed by multiple threads.
The thread is on the inside, probably started in the constructor.  Would you make all other methods besides ctor/dtor const?  There's no const-ctor in C++.
> Not the way I'm thinking of it.  I was thinking you might pimpl-ize just the parts that need to have stable addresses, e.g. use a unique_ptr<Mutex> internally.
But that won't work!  When the encapsulated thread wakes up and locks the mutex, he'll need a way back to the object.  You'd need structure with:
* the mutex
* any condition variables
* a pointer back to the object - protected by mutex, modified by move
Your thread also must be outside any non-static member functions of the object unless it has the lock.  It can't loop in moster::run if _this_ can be changed.
The unmovable version might have had state that was private to the worker and could be modified outside a lock.  So you've either increased contention on the mutex, or might try introducing a special move mutex which adds its own complexity.
BTW that move support structure wouldn't itself be movable .  I don't know if that breaks your style or not.
> > Let the user decide how ownership should work.  
> Another way to say that is "make the user decide how ownership should work."
Isn't that how C++ works?  The use decides where in memory to construct an object.  The object decides if it's movable.
> > Here's a nice example: boost::asio::io_service isn't movable.  Should 
> > it be?
> Maybe; I don't know that class real well.  Has ASIO been move-enabled at all?
sockets: move but no copy - which we agree with
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/basic_stream_socket.html
io_service: no move, no copy 
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service/io_service.html
stand
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service__strand/strand.html
no copy; no move.   Makes sense to me since it's a synchronization object like 
io_service is passed as non-const & to all sockets, which probably isn't your style since the user has to make sure io_service outlives its users.  I'm guessing you'd perfer shared_ptrs.
Dave, thanks for intruducing me to your "maximum mobility C++".  I hope I haven't been too frustrating.
Chris