
22 Jun
2012
22 Jun
'12
4:03 p.m.
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