Iterating over the output of a member function in std::for_each
-
14-11-2019 - |
Question
I have a class with an accessor member function that I want to call and apply the result to a functor using std::for_each. I have a working version below that uses a for loop and for_each, but the for_each version is cryptic and cumbersome. Is there a way I can make the for_each version more concise, considering I have access to boost, but not C++11?
#if 0
// for loop version:
for(value_vector_type::iterator it = values.begin(); it!=values.end(); it++){
avg(it->getValue()); // I want to put this in a for_each loop
}
#else
// bind version:
std::for_each(values.begin(), values.end(), // iterate over all values
boost::bind(
boost::mem_fn(&average_type::operator()), // attach the averaging functor to the output of the getvalue call
&avg,
boost::bind(
boost::mem_fn(&value_wrapper_type::getValue), // bind the getValue call to each element in values
_1
)
)
);
#endif
Here is the full working implementation:
#include <vector>
#include <algorithm>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/bind/mem_fn.hpp>
// A value wrapper
template<typename T>
struct Value {
Value(){}
Value(const T& value, bool valid = true):m_value(value),m_valid(valid){}
T getValue(){ return m_value; }
bool getValid(){ return m_valid; }
void setValue(const T& value){ m_value = value; }
void setValid(const T& valid){ m_valid = valid; }
private:
T m_value;
bool m_valid;
};
// Class that calculates the average piecewise
template<typename T>
struct Average {
private:
T m_numPoints;
T m_ChannelSum;
public:
Average() : m_numPoints(0), m_ChannelSum(0.0){}
void operator()(T value){
m_numPoints++;
m_ChannelSum+=value;
}
double getAverage(){ return m_ChannelSum/m_numPoints; }
T getCount(){ return m_numPoints; }
T getSum(){ return m_ChannelSum; }
};
// Run the average computation on several values
int main(int argc, char** argv){
typedef int value_type;
typedef Value<value_type> value_wrapper_type;
typedef std::vector<value_wrapper_type> value_vector_type;
value_vector_type values;
values.push_back(value_wrapper_type(5));
values.push_back(value_wrapper_type(7));
values.push_back(value_wrapper_type(3));
values.push_back(value_wrapper_type(1));
values.push_back(value_wrapper_type(2));
typedef Average<value_type> average_type;
average_type avg;
#if 0
// for loop version:
for(value_vector_type::iterator it = values.begin(); it!=values.end(); it++){
avg(it->getValue()); // I want to put this in a for_each loop
}
#else
// bind version:
std::for_each(values.begin(), values.end(), // iterate over all values
boost::bind(
boost::mem_fn(&average_type::operator()), // attach the averaging functor to the output of the getvalue call
&avg,
boost::bind(
boost::mem_fn(&value_wrapper_type::getValue), // bind the getValue call to each element in values
_1
)
)
);
#endif
std::cout << "Average: " << avg.getAverage() << " Count: " << avg.getCount() << " Sum: " << avg.getSum() << std::endl;
}
note: my original question was how to construct a for_each at all, but I've found that solution and a whole new question did not make much sense.
Thanks, all help is really appreciated!
Solution 4
Credit goes to Mathias Gaunard on the boost.users mailing list for pointing me towards this solution:
std::for_each(values.begin(), values.end(),
boost::bind(boost::ref(avg), boost::bind(&value_wrapper_type::getValue, _1))
);
Wrapping avg
with boost::ref
is required because otherwise a copy of avg
is filled out with the results of getValue()
, rather than avg
itself.
Here is the full compiled and tested solution:
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/bind/mem_fn.hpp>
// A value wrapper
template<typename T>
struct Value {
Value(){}
Value(const T& value, bool valid = true):m_value(value),m_valid(valid){}
T getValue(){ return m_value; }
bool getValid(){ return m_valid; }
void setValue(const T& value){ m_value = value; }
void setValid(const T& valid){ m_valid = valid; }
private:
T m_value;
bool m_valid;
};
// Class that calculates the average piecewise
template<typename T>
struct Average {
private:
T m_numPoints;
T m_ChannelSum;
public:
typedef void result_type;
Average() : m_numPoints(0), m_ChannelSum(0.0){}
result_type operator()(T value){
m_numPoints++;
m_ChannelSum+=value;
}
double getAverage(){
if (m_ChannelSum==0) {
throw std::logic_error("Cannot get average of zero values");
}
return m_ChannelSum/m_numPoints;
}
T getCount(){ return m_numPoints; }
T getSum(){ return m_ChannelSum; }
};
// Run the average computation on several values
int main(int argc, char** argv){
typedef int value_type;
typedef Value<value_type> value_wrapper_type;
typedef std::vector<value_wrapper_type> value_vector_type;
value_vector_type values;
values.push_back(value_wrapper_type(5));
values.push_back(value_wrapper_type(7));
values.push_back(value_wrapper_type(3));
values.push_back(value_wrapper_type(1));
values.push_back(value_wrapper_type(2));
typedef Average<value_type> average_type;
average_type avg;
#if 0
// for loop version:
for(value_vector_type::iterator it = values.begin(); it!=values.end(); it++){
avg(it->getValue()); // I want to put this in a for_each loop
}
#else
// bind version:
std::for_each(values.begin(), values.end(),
boost::bind(boost::ref(avg), boost::bind(&value_wrapper_type::getValue, _1))
);
#endif
std::cout << "Average: " << avg.getAverage() << " Count: " << avg.getCount() << " Sum: " << avg.getSum() << std::endl;
}
OTHER TIPS
If you don't have C++11 but Boost you could try a bind()
expression (which would also work with C++2011 as bind()
is part of C++2011):
std::for_each(a.begin(), a.end(), bind(&avg<value_type>, bind(&Value<value_type>::getValue, _1)));
if you are using c++11 then you can try
for(auto& a: values)
avg(a->getValue());
or
std::for_each(a.begin(), a.end(), [](whatever_type& wt){
avg(wt->getValue());
});
If you are not, then I think that toy have as good as your going to get although formatting wont hurt.
for(value_vector_type::iterator it = values.begin();
it!=values.end();
++it)
{
avg(it.getValue()); // I want to put this in a for_each loop
}
Trying to be too clever with function object and the like can often have the inverse effect of obscuring your code.
One way to make it look neater is to use Boost.Phoenix. You can shorten down to this:
std::for_each(values.begin(), values.end(), lazy(avg)(arg1.getValue()));
Heres how to do that. First thing you need to do is make the avg
function object lazy. The simplest way to that is in-place with a function, defined like this:
template<class Function>
function<Function> lazy(Function x)
{
return function<Function>(x);
}
Next thing you need to do is write a function object for getValue, that can be lazy, like this:
struct get_value_impl
{
// result_of protocol:
template <typename Sig>
struct result;
template <typename This, typename T>
struct result<This(Value<T>&)>
{
// The result will be T
typedef typename T type;
};
template <typename V>
typename result<get_value_impl(V &)>::type
operator()(V & value) const
{
return value.getValue();
}
};
Thirdly, we extend the phoenix actors, using our get_value_impl
class, so it will have a getValue
method, like this:
template <typename Expr>
struct value_actor
: actor<Expr>
{
typedef actor<Expr> base_type;
typedef value_actor<Expr> that_type;
value_actor( base_type const& base )
: base_type( base ) {}
typename expression::function<get_value_impl, that_type>::type const
getValue() const
{
function<get_value_impl> const f = get_value_impl();
return f(*this);
}
};
Finally, we put it all together by defining the argument and passing it into the for_each algorithm:
expression::terminal<phoenix::argument<1>, value_actor> arg1;
std::for_each(values.begin(), values.end(), lazy(avg)(arg1.getValue()));
If you can use boost, but not C++11 features, then I would consider using the BOOST_FOREACH macro
Yes, it's a macro, but as macros go it's well behaved
It's also reads quite nicely and is hard to get wrong
BOOST_FOREACH(const Value& rValue, values)
{
avg(rValue.getValue());
}
C++11 range based for loops will replace it