Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r66360 - sandbox/function/boost/function
From: dsaritz_at_[hidden]
Date: 2010-11-02 15:45:10


Author: psiha
Date: 2010-11-02 15:45:08 EDT (Tue, 02 Nov 2010)
New Revision: 66360
URL: http://svn.boost.org/trac/boost/changeset/66360

Log:
Refactored (copy) construction and destruction code to avoid creating unnecessary EH states (and related bloat).
Properly encapsulated function_base data members.
Minor other refactoring and stylistic changes.
Text files modified:
   sandbox/function/boost/function/function_base.hpp | 70 ++++++++++++++++++++++++++++++++--------
   sandbox/function/boost/function/function_template.hpp | 30 ++++++----------
   2 files changed, 67 insertions(+), 33 deletions(-)

Modified: sandbox/function/boost/function/function_base.hpp
==============================================================================
--- sandbox/function/boost/function/function_base.hpp (original)
+++ sandbox/function/boost/function/function_base.hpp 2010-11-02 15:45:08 EDT (Tue, 02 Nov 2010)
@@ -1148,14 +1148,54 @@
   template <class EmptyHandler>
   class safe_mover;
 
-public:
+protected:
+ function_base() { detail::function::debug_clear( *this ); }
+ function_base( function_base const & other )
+ {
+ detail::function::debug_clear( *this );
+ assign_boost_function_direct( other );
+ }
+
     template <class EmptyHandler>
     function_base( detail::function::vtable const & empty_handler_vtable, EmptyHandler )
     {
         detail::function::debug_clear( *this );
         this->clear<true, EmptyHandler>( empty_handler_vtable );
     }
- ~function_base() { destroy(); }
+
+ // Implementation note:
+ // Ideally destruction/cleanup could be simply placed into the
+ // function_base destructor. However, this has unfortunate efficiency
+ // implications because it creates unnecessary EH states (=unnecessary code)
+ // in non-trivial (i.e. fallible/throwable) constructors of derived classes.
+ // In such cases the compiler has to generate EH code to call the
+ // function_base destructor if the derived-class constructor fails after
+ // function_base is already constructed. This is completely redundant
+ // because initially function_base is/was always initialized with the empty
+ // handler for which no destruction is necessary but the compiler does not
+ // see this because of the indirect vtable call.
+ // Because of the above issue, default constructed function_base objects
+ // with a null/uninitialized vtable pointer are allowed and the duty to
+ // either throw an exception or properly initialized the vtable (along with
+ // the entire object) is shifted to the derived class destructor.
+ // (02.11.2010.) (Domagoj Saric)
+ // Implementation note:
+ // A by-the-book protected (empty) destructor is still added to prevent
+ // accidental deletion of a function_base pointer. However, because even a
+ // trivial empty destructor creates EH states with MSVC++ (even version 10)
+ // and possibly other compilers, it is removed from release builds.
+ // (02.11.2010.) (Domagoj Saric)
+ /// \todo Devise a cleaner way to deal with all of this (maybe move/add more
+ /// template methods to function_base so that it can call assign methods
+ /// from its template constructors thereby moving all construction code
+ /// here).
+ /// (02.11.2010.) (Domagoj Saric)
+#ifdef _DEBUG
+ ~function_base()
+ {
+ //...destroy();
+ }
+#endif // _DEBUG
 
   template <class EmptyHandler>
   void swap( function_base & other, detail::function::vtable const & empty_handler_vtable );
@@ -1227,6 +1267,11 @@
       return *p_vtable_;
   }
 
+ detail::function::function_buffer & functor() const
+ {
+ return functor_;
+ }
+
   template <bool direct, class EmptyHandler>
   BF_NOTHROW
   void clear( detail::function::vtable const & empty_handler_vtable )
@@ -1248,8 +1293,8 @@
 private: // Assignment from another boost function helpers.
   void assign_boost_function_direct( function_base const & source )
   {
- source.p_vtable_->clone( source.functor_, this->functor_ );
- p_vtable_ = source.p_vtable_;
+ source.get_vtable().clone( source.functor_, this->functor_ );
+ p_vtable_ = &source.get_vtable();
   }
 
   template <class EmptyHandler>
@@ -1335,10 +1380,10 @@
       this->swap<EmptyHandler>( tmp, empty_handler_vtable );
   }
 
-private:
+protected:
     void destroy() { get_vtable().destroy( this->functor_ ); }
 
-protected:
+private:
   // Fix/properly encapsulate these members and use the function_buffer_holder.
           detail::function::vtable const * p_vtable_;
   mutable detail::function::function_buffer functor_ ;
@@ -1703,14 +1748,11 @@
     else
     if ( direct )
     {
- BOOST_ASSERT
- (
- ( this->p_vtable_ == &empty_handler_vtable ) ||
- (
- is_same<EmptyHandler, FunctionObj>::value &&
- this->p_vtable_ == NULL
- )
- );
+ // Implementation note:
+ // See the note for the function_base destructor as to why a null
+ // vtable is allowed here.
+ // (02.11.2010.) (Domagoj Saric)
+ BOOST_ASSERT( this->p_vtable_ == NULL );
         typedef typename get_functor_manager<FunctionObj, Allocator>::type functor_manager;
         functor_manager::assign( f, this->functor_, a );
         this->p_vtable_ = &functor_vtable;

Modified: sandbox/function/boost/function/function_template.hpp
==============================================================================
--- sandbox/function/boost/function/function_template.hpp (original)
+++ sandbox/function/boost/function/function_template.hpp 2010-11-02 15:45:08 EDT (Tue, 02 Nov 2010)
@@ -150,24 +150,19 @@
         = default_policies
     #endif // BOOST_FUNCTION_MAX_ARGS > 10
>
- class BOOST_FUNCTION_FUNCTION : public function_base
-
+ class BOOST_FUNCTION_FUNCTION
+ : public function_base
 #if BOOST_FUNCTION_NUM_ARGS == 1
-
     , public std::unary_function<T0,R>
-
 #elif BOOST_FUNCTION_NUM_ARGS == 2
-
     , public std::binary_function<T0,T1,R>
-
 #endif
-
   {
   private: // Actual policies deduction section.
     //mpl::at<AssocSeq,Key,Default> does not yet exist so...:
 
- typedef throw_on_empty default_empty_handler ;
- typedef mpl::false_ default_nothrow_policy;
+ typedef throw_on_empty default_empty_handler ;
+ typedef mpl::false_ default_nothrow_policy;
 
     typedef typename mpl::at<PolicyList, empty_handler_t>::type user_specified_empty_handler ;
     typedef typename mpl::at<PolicyList, is_no_throw_t >::type user_specified_nothrow_policy;
@@ -261,6 +256,8 @@
         #endif // BOOST_MSVC
     }
 
+ ~BOOST_FUNCTION_FUNCTION() { function_base::destroy(); }
+
     // MSVC chokes if the following two constructors are collapsed into
     // one with a default parameter.
     template <typename Functor>
@@ -274,8 +271,6 @@
                         int>::type = 0
         #endif // BOOST_NO_SFINAE
     )
- :
- function_base( empty_handler_vtable(), base_empty_handler() )
     {
       this->do_assign<true, Functor>( f );
     }
@@ -292,8 +287,6 @@
                         int>::type = 0
         #endif // BOOST_NO_SFINAE
     )
- :
- function_base( empty_handler_vtable(), base_empty_handler() )
     {
       this->do_assign<true, Functor>( f, a );
     }
@@ -308,10 +301,9 @@
     }
 #endif
 
- BOOST_FUNCTION_FUNCTION(const BOOST_FUNCTION_FUNCTION& f) : function_base( empty_handler_vtable(), base_empty_handler() )
- {
- this->do_assign<true>( f );
- }
+ BOOST_FUNCTION_FUNCTION( BOOST_FUNCTION_FUNCTION const & f )
+ : function_base( static_cast<function_base const &>( f ) )
+ {}
 
     /// Determine if the function is empty (i.e., has empty target).
     bool empty() const { return p_vtable_->is_empty_handler_vtable(); }
@@ -517,14 +509,14 @@
     result_type do_invoke( BOOST_FUNCTION_PARMS BOOST_FUNCTION_COMMA mpl::true_ /*this call*/ ) const
     {
         typedef result_type (detail::function::function_buffer::* invoker_type)(BOOST_FUNCTION_TEMPLATE_ARGS);
- return (functor_.*(get_vtable(). BOOST_NESTED_TEMPLATE invoker<invoker_type>()))(BOOST_FUNCTION_ARGS);
+ return (functor().*(get_vtable(). BOOST_NESTED_TEMPLATE invoker<invoker_type>()))(BOOST_FUNCTION_ARGS);
     }
 
     BF_FORCEINLINE
     result_type do_invoke( BOOST_FUNCTION_PARMS BOOST_FUNCTION_COMMA mpl::false_ /*free call*/ ) const
     {
         typedef result_type (* invoker_type)( BOOST_FUNCTION_TEMPLATE_ARGS BOOST_FUNCTION_COMMA detail::function::function_buffer & );
- return get_vtable(). BOOST_NESTED_TEMPLATE invoker<invoker_type>()( BOOST_FUNCTION_ARGS BOOST_FUNCTION_COMMA functor_ );
+ return get_vtable(). BOOST_NESTED_TEMPLATE invoker<invoker_type>()( BOOST_FUNCTION_ARGS BOOST_FUNCTION_COMMA functor() );
     }
 
 


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk