/////////////////////////////////////////////////////////////////////////////// | |
// 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 &r) | |
{ | |
::new(this->cache.address()) result_type(r); | |
} | |
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 |