Boost logo

Boost :

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


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