Subject: Re: [boost] [function] function wrapping with no exception safetyguarantee
From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2010-10-21 20:32:51
On Thu, Oct 21, 2010 at 7:33 AM, Stewart, Robert <Robert.Stewart_at_[hidden]> wrote:
> Daniel Walker wrote:
>> Nevin Liber wrote:
>> > Daniel Walker wrote:
>> >> If the call precondition is not met and boost::function::operator()
>> >> attempts to call the target function, then the program could crash.
>> > In the case of raw function pointers (either NULL or uninitialized),
>> > from that point on the program can do whatever the heck it wants to
>> > do, since it is now in the realm of undefined behavior. Heck, you
>> > can no longer guarantee that *any* object in your system is still in
>> > a consistent, let alone correct state.
>> True, and in the worst case scenario, the program could crash.
> No. The worst case scenario is that the program continues to run and does all sorts of wrong things like sending market orders for too many stocks at an enormous price or overdoses a patient.
Oh man. That would be awful!
>> >> Instead, under the current implementation,
>> >> boost::function::operator() checks the call precondition
>> > What call precondition? It is perfectly legitimate to call
>> > operator() on a default constructed boost::function; it has
>> > well defined semantics.
>> In order to call a function using boost::function, the following
>> precondition must be met: boost::function must be non-empty. The
> Not true. That is not a precondition. In order to successfully invoke a function, there must be a function to invoke, but boost::function's function call operator behaves as documented when there is no function.
> By contrast, a precondition is a condition that the caller must satisfy in order for the called function to behave as documented. Violating the precondition leaves the function's behavior wide open.
I think in the case of function wrappers it helps to make a
distinction between what I call a "call precondition" and an
"exception safety precondition." A call precondition is a condition
that must be met in order to invoke a function. One example would be,
as you put it, "there must be a function to invoke;" i.e. the wrapper
has to be assigned a target. Here's another example: Imagine we had a
remote function wrapper so that remote function calls could be used
interchangeable with other wrapped functions. Then a call precondition
of the remote function wrapper would be that the remote host is
available. Or here's a simpler example: if you intend to invoke a
function through a function pointer, a call precondition is that the
function pointer is not null.
An exception safety precondition applies after a function has been
called and is running. And I like your wording, it is "a condition
that the caller must satisfy in order for the called function to
behave as documented" with respect to exception safety.
I think the reason the distinction is important is that function
objects do not normally have call preconditions; i.e. the function is
always there, and when operator() is invoked, control is passed to the
function. With function wrappers, however, there is a level of
indirection. When a function wrapper is invoked, control is not
instantly passed to the wrapped function. This gives rise to some
trade-offs. With boost::function, the call precondition is handled
transparently with respect to strong exception safety such that
calling a wrapped function is the same as calling the function
directly: Either an exception will be thrown or the function will
complete successfully given the target function's exception safety
preconditions. With unsafe_function, the call precondition is an
_additional_ exception safety precondition, and so, unlike
boost::function, its behavior is not documented (or defined) when the
call precondition is not met.
> I hope this helps, at least by clarifying terminology.
Yes, thanks, I think it does help. You're using the term
"precondition" in a broad sense, which is perfectly fine. But I think
making a distinction between a condition needed to invoke a function
and a condition needed while running a function helps when discussing
the difference between boost::function and unsafe_function.
boost::function and unsafe_function have different "preconditions" in
the broad sense (they have different exception safety preconditions, I
would say), but they have the same call precondition -- a condition
that must be met to actually invoke a target function.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk