Boost logo

Boost :

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


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

> Dave Abrahams wrote:
>> > "Deferred Callable Object"
>> > http://www.boost.org/doc/libs/1_49_0/libs/fusion/doc/html/fusion/functional/concepts/def_callable.html
>> Sorry, I don't follow any of this. When you say "functors," what do
>> you mean?
>
> You're the boost guy; I think the ontology in fusion is more than enough.

I don't follow this either.

> Perhaps it's abuse of the term, but I use "functor" to mean callable,
> usually copyable object (including function pointer).
>
>> That word has a formal meaning in computer science that is probably
>> not what you mean, and an informal meaning in C++ (just another word
>> for "function object") that doesn't /seem/ to be what you mean.
> http://en.wikipedia.org/wiki/Functor
> "For functors as a synonym of 'function objects'"
>
> I think we're talking about the same thing. Would you perfer I use
> "function object"?

Frankly, I would (the C++ community never should have started using
"functor" that way), but that's not the point. You seemed to mean a
particular kind of function object; a dynamically-polymorphic kind,
i.e. one with type erasure, e.g. boost::function or std::function. Now
it seems you mean the term more generally.

Your earlier reference to fusion makes it seem like when anyone says
"polymorphic" you automatically think of static polymorphism. Most
people think of dynamic polymorphism, especially when the word "runtime"
precedes "polymorphic."

>>> > 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);
>>> > ...
>>> > };
>
>> > It's got threads, io_service, mutexes, signal2, containers, sockets,
>> > etc inside it that don't like being moved/copied and don't need to be.
>> > What would you do Dave?
>
>> I wouldn't write a monster in the first place.
>
> You believe in OOP encapsulation (separation of problems), right?

I believe in both encapsulation and separation of concerns, but on OOP I
reserve judgement.

> I think if you had this kind of problem, you'd also try to hide the
> details behind some minimal interface. I can understand you proposing
> a better interface.
>
> I can't imagine you saying "C++ programmers shouldn't do network
> programming".

Me neither.

> So imagine a good C++ programmer like has yucky details to encapsulate:
> * networking protocol
> * TCP sockets - to multiple servers
> * UDP/multicast sockets - which should join/leave when needed
> * containers for subscriptions - say B's have ids
> * unsubscribe - optionally lazy
> * threads - single or pool of threads
>
> All the users care about is subscribing and getting updates. What
> should the interface look like?

It should be movable at least.

> Was I that far off? Did I try to hide too much?

I don't think of it that way.

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

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

>> Is that generally bad? Do you think all objects be copy/movable?
>
>> I think it's 99.9% reasonable to make all objects movable, and maybe
>> 75% reasonable to make all objects copyable. You can do either or
>> both of these, the difference is a matter of degree: just how
>> uncompromising do you have to be to build a world that makes sense?
>
> A good candidate for the 25% is any object that is supposed to
> represent and external resource.

Yes. Those are typically movable and non-copyable in the standard
library (e.g. thread, mutex, ...).

> Example: a TCP session with some other process. It makes sense to
> have a non-copyable object which exclusively owns that session whose
> deconstructor logs out and disconnects. You can copy the fd. You
> could connect a second time. You probably can not tell the server on
> the other side to copy all the state from associated with one session
> to another.
>
> 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."

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

> It might be done easliy by smart_ptr<> pimple but that costs something
> too.

Yes, but not much compared to everything your monster has to pay in
taxes and protection money just to stay in business.

> It's also something a user of an object can do.

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.

> Let the user decide how ownership should work.

Another way to say that is "make the user decide how ownership should
work."

> Anyway such a pimple type though private would still be a 0.1% type.
>
> 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?

> I wouldn't call asio legacy code. To me it appears to be the latest
> style of interface.

By "legacy code" I just mean "existing code that isn't move-enabled."

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