Boost logo

Boost :

Subject: Re: [boost] Formal Review Request: TypeErasure
From: Dave Abrahams (dave_at_[hidden])
Date: 2012-06-22 14:13:27


on Fri Jun 22 2012, "Hite, Christopher" <Christopher.Hite-AT-partner.commerzbank.com> wrote:

> 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.

"Deep" is not a relevant term in a value semantics world (in fact, it
will break your ability to think about values). Does it have a
modifiable state (value)? If so it's mutable. If it has no modifiable
state, then it's immutable.

> Users can change that state. However someone could say a
> shared_monster is immutable the pointer to its guts can't be changed.

If you pimplize it the way you've described, you have shared mutable
guts and so its guts can be changed even if the shared_ptr is const.

>>> 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?

Because it limits interoperability, and encourages people to pimpl-ize
the whole thing, which encourages reference semantics and implicit
sharing, and is harder to reason about.

> If we start off with an unmovable object which does no dynamic
> allocation. A movable verson probably forces it on the user.

Not necessarily. It depends if you allow it to be moved after the
thread is started.

>> 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.

Sorry; locks are movable but mutexes aren't.

>> > 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++.

If the thread is necessarily started in the ctor, and the move ctor
moves the mutex, then the move ctor is necessarily broken and you might
as well disable it.

>> 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.

Then don't move the object while there are multiple threads accessing
it. If the threads are always alive, you simply can't move it and you
might as well not make it movable.

>> > 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.

The object may also decide where in memory to construct its parts.

>> > 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.

Not at all. I learned something, too.

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

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