
On Thu, Oct 21, 2010 at 7:05 PM, Daniel Walker <daniel.j.walker@gmail.com> wrote:
On Thu, Oct 21, 2010 at 6:13 PM, Edward Diener <eldiener@tropicsoft.com> wrote:
On 10/21/2010 6:05 PM, Daniel Walker wrote:
Right. I don't mean the management system has a bug, I mean that it has encountered a problem, for example, cloning the target. So, boost::function could be empty because it has never been assigned a target or because the most recent attempt to assign a target failed... or because it was cleared by the user calling clear(). Are those all of the scenarios that can lead to an empty boost::function?
I don't mean to question the design of boost::function but wouldn't the inability of cloning a target, or assign a target, be a problem which should lead to an exception being thrown ?
Right, but suppose boost::function was instantiated outside of the try block where the assignment fails. Then after the exception is thrown and handled, the boost::function object could still be used.
But actually, I just noticed that, even though the portable function wrappers are empty after an assignment failure, boost::function is not actually cleared. So, in fact, a failed assignment is NOT a scenario that leads to an empty state, as I first thought. This is probably an oversight/bug in the portable wrappers, right?
I would assume that boost::function not having a target would normally only occur if no target had been set or if the user removed a target which had been set.
Right, and indeed, this appears to be the case... unless there's some other scenario we overlooked...
Oops. Sorry, I spoke too soon. In fact, it IS currently possible for a boost::function object to become empty due to a failed assignment. It happens because the small object manager clones the target during a call to swap(). If there is an exception during the allocation, boost::function handles it, sets itself to empty and rethrows. Here's an example that demonstrates the behavior. #include <cassert> #include <iostream> #include <boost/function.hpp> struct S0 { void operator()() {} }; int i = 0; struct S1 { void operator()() {} void* operator new(std::size_t, void*) { // throw on third alloc if(++i == 3) throw std::bad_alloc(); } }; int main() { boost::function<void()> g = S0(); assert(g); // assertion ok, since g is not empty. try { g = S1(); } catch(std::exception& e) { std::cerr << "failed function assignment: " << e.what() << std::endl; } assert(g); // now assert fails, since g is empty. return 0; } Here's a backtrace just before the bad_alloc is thrown. #0 S1::operator new () at function_assignment_test.cpp:15 #1 0x00000001000019e1 in boost::detail::function::functor_manager_common<S1>::manage_small (in_buffer=@0x7fff5fbff298, out_buffer=@0x7fff5fbff378, op=boost::detail::function::move_functor_tag) at function_base.hpp:318 #2 0x0000000100001abf in boost::detail::function::functor_manager<S1>::manager (in_buffer=@0x7fff5fbff298, out_buffer=@0x7fff5fbff378, op=boost::detail::function::move_functor_tag) at function_base.hpp:364 #3 0x0000000100001aeb in boost::detail::function::functor_manager<S1>::manager (in_buffer=@0x7fff5fbff298, out_buffer=@0x7fff5fbff378, op=boost::detail::function::move_functor_tag) at function_base.hpp:412 #4 0x0000000100001b44 in boost::detail::function::functor_manager<S1>::manage (in_buffer=@0x7fff5fbff298, out_buffer=@0x7fff5fbff378, op=boost::detail::function::move_functor_tag) at function_base.hpp:440 #5 0x0000000100001d25 in boost::function0<void>::move_assign (this=0x7fff5fbff370, f=@0x7fff5fbff290) at function_template.hpp:974 #6 0x0000000100001dc7 in boost::function0<void>::swap (this=0x7fff5fbff300, other=@0x7fff5fbff370) at function_template.hpp:848 #7 0x0000000100001e2a in boost::function<void ()()>::operator=<S1> (this=0x7fff5fbff370, f={<No data fields>}) at function_template.hpp:1105 #8 0x0000000100001221 in main () at function_assignment_test.cpp:25 Daniel Walker