text: ///////////////////////////////////////////////////////////////////////////////
  // droppable_accumulator.hpp
  //
  //  Copyright 2005 Eric Niebler. Distributed under the Boost
  //  Software License, Version 1.0. (See accompanying file
  //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  
  #ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005
  #define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005
  
  #include <new>
  #include <boost/assert.hpp>
  #include <boost/mpl/apply.hpp>
  #include <boost/aligned_storage.hpp>
  #include <boost/accumulators/framework/depends_on.hpp> // for feature_of
  #include <boost/accumulators/framework/parameters/accumulator.hpp> // for accumulator
  
  namespace boost { namespace accumulators
  {
  
      template<typename Accumulator>
      struct droppable_accumulator;
  
      namespace detail
      {
          ///////////////////////////////////////////////////////////////////////////////
          // add_ref_visitor
          //   a fusion function object for add_ref'ing accumulators
          template<typename Args>
          struct add_ref_visitor
          {
              explicit add_ref_visitor(Args const &args)
                : args_(args)
              {
              }
  
              template<typename Accumulator>
              void operator ()(Accumulator &acc) const
              {
                  typedef typename Accumulator::feature_tag::dependencies dependencies;
  
                  acc.add_ref(this->args_);
  
                  // Also add_ref accumulators that this feature depends on
                  this->args_[accumulator].template
                      visit_if<detail::contains_feature_of_<dependencies> >(
                          *this
                  );
              }
  
          private:
              add_ref_visitor &operator =(add_ref_visitor const &);
              Args const &args_;
          };
  
          template<typename Args>
          add_ref_visitor<Args> make_add_ref_visitor(Args const &args)
          {
              return add_ref_visitor<Args>(args);
          }
  
          ///////////////////////////////////////////////////////////////////////////////
          // drop_visitor
          //   a fusion function object for dropping accumulators
          template<typename Args>
          struct drop_visitor
          {
              explicit drop_visitor(Args const &args)
                : args_(args)
              {
              }
  
              template<typename Accumulator>
              void operator ()(Accumulator &acc) const
              {
                  if(typename Accumulator::is_droppable())
                  {
                      typedef typename Accumulator::feature_tag::dependencies dependencies;
  
                      acc.drop(this->args_);
                      // Also drop accumulators that this feature depends on
                      this->args_[accumulator].template
                          visit_if<detail::contains_feature_of_<dependencies> >(
                              *this
                      );
                  }
              }
  
          private:
              drop_visitor &operator =(drop_visitor const &);
              Args const &args_;
          };
  
          template<typename Args>
          drop_visitor<Args> make_drop_visitor(Args const &args)
          {
              return drop_visitor<Args>(args);
          }
      }
  
      //////////////////////////////////////////////////////////////////////////
      // droppable_accumulator_base
      template<typename Accumulator>
      struct droppable_accumulator_base
        : Accumulator
      {
          typedef droppable_accumulator_base base;
          typedef mpl::true_ is_droppable;
          typedef typename Accumulator::result_type result_type;
  
          template<typename Args>
          droppable_accumulator_base(Args const &args)
            : Accumulator(args)
            , ref_count_(0)
          {
          }
  
          template<typename Args>
          void operator ()(Args const &args)
          {
              if(!this->is_dropped())
              {
                  this->Accumulator::operator ()(args);
              }
          }
  
          template<typename Args>
          void add_ref(Args const &)
          {
              ++this->ref_count_;
          }
  
          template<typename Args>
          void drop(Args const &args)
          {
              BOOST_ASSERT(0 < this->ref_count_);
              if(1 == this->ref_count_)
              {
                  static_cast<droppable_accumulator<Accumulator> *>(this)->on_drop(args);
              }
              --this->ref_count_;
          }
  
          bool is_dropped() const
          {
              return 0 == this->ref_count_;
          }
  
      private:
          int ref_count_;
      };
  
      //////////////////////////////////////////////////////////////////////////
      // droppable_accumulator
      //   this can be specialized for any type that needs special handling
      template<typename Accumulator>
      struct droppable_accumulator
        : droppable_accumulator_base<Accumulator>
      {
          template<typename Args>
          droppable_accumulator(Args const &args)
            : droppable_accumulator::base(args)
          {
          }
      };
  
      //////////////////////////////////////////////////////////////////////////
      // with_cached_result
      template<typename Accumulator>
      struct with_cached_result
        : Accumulator
      {
          typedef typename Accumulator::result_type result_type;
  
          template<typename Args>
          with_cached_result(Args const &args)
            : Accumulator(args)
            , cache()
          {
          }
  
          with_cached_result(with_cached_result const &that)
            : Accumulator(*static_cast<Accumulator const *>(&that))
            , cache()
          {
              if(that.has_result())
              {
                  this->set(that.get());
              }
          }
  
          ~with_cached_result()
          {
              // Since this is a base class of droppable_accumulator_base,
              // this destructor is called before any of droppable_accumulator_base's
              // members get cleaned up, including is_dropped, so the following
              // call to has_result() is valid.
              if(this->has_result())
              {
                  this->get().~result_type();
              }
          }
  
          template<typename Args>
          void on_drop(Args const &args)
          {
              // cache the result at the point this calcuation was dropped
              BOOST_ASSERT(!this->has_result());
              this->set(this->Accumulator::result(args));
          }
  
          template<typename Args>
          result_type result(Args const &args) const
          {
              return this->has_result() ? this->get() : this->Accumulator::result(args);
          }
  
      private:
          with_cached_result &operator =(with_cached_result const &);
  
          void set(result_type const &result)
          {
              ::new(this->cache.address()) result_type(result);
          }
  
          result_type const &get() const
          {
              return *static_cast<result_type const *>(this->cache.address());
          }
  
          bool has_result() const
          {
              typedef with_cached_result<Accumulator> this_type;
              typedef droppable_accumulator_base<this_type> derived_type;
              return static_cast<derived_type const *>(this)->is_dropped();
          }
  
          aligned_storage<sizeof(result_type)> cache;
      };
  
      namespace tag
      {
          template<typename Feature>
          struct as_droppable
          {
              typedef droppable<Feature> type;
          };
  
          template<typename Feature>
          struct as_droppable<droppable<Feature> >
          {
              typedef droppable<Feature> type;
          };
  
          //////////////////////////////////////////////////////////////////////////
          // droppable
          template<typename Feature>
          struct droppable
            : as_feature<Feature>::type
          {
              typedef typename as_feature<Feature>::type feature_type;
              typedef typename feature_type::dependencies tmp_dependencies_;
  
              typedef
                  typename mpl::transform<
                      typename feature_type::dependencies
                    , as_droppable<mpl::_1>
                  >::type
              dependencies;
  
              struct impl
              {
                  template<typename Sample, typename Weight>
                  struct apply
                  {
                      typedef
                          droppable_accumulator<
                              typename mpl::apply2<typename feature_type::impl, Sample, Weight>::type
                          >
                      type;
                  };
              };
          };
      }
  
      // make droppable<tag::feature(modifier)> work
      template<typename Feature>
      struct as_feature<tag::droppable<Feature> >
      {
          typedef tag::droppable<typename as_feature<Feature>::type> type;
      };
  
      // make droppable<tag::mean> work with non-void weights (should become
      // droppable<tag::weighted_mean>
      template<typename Feature>
      struct as_weighted_feature<tag::droppable<Feature> >
      {
          typedef tag::droppable<typename as_weighted_feature<Feature>::type> type;
      };
  
      // for the purposes of feature-based dependency resolution,
      // droppable<Foo> provides the same feature as Foo
      template<typename Feature>
      struct feature_of<tag::droppable<Feature> >
        : feature_of<Feature>
      {
      };
  
      // Note: Usually, the extractor is pulled into the accumulators namespace with
      // a using directive, not the tag. But the droppable<> feature doesn't have an
      // extractor, so we can put the droppable tag in the accumulators namespace
      // without fear of a name conflict.
      using tag::droppable;
  
  }} // namespace boost::accumulators
  
  #endif
  