
On Thu, Oct 21, 2010 at 7:33 AM, Stewart, Robert <Robert.Stewart@sig.com> 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.
<snip> 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. Daniel Walker