Need prettier solution of below example but with std::accumulate.
#include <algorithm>
#include <vector>
#include <iostream>
class Object
{
public:
Object( double a, double b ):
a_( a ),
b_( b )
{}
double GetA() const { return a_; }
double GetB() const { return b_; }
// other methods
private:
double a_;
double b_;
};
class Calculator
{
public:
Calculator( double& result ):
result_( result )
{}
void operator() ( const Object& object )
{
// some formula
result_ += object.GetA() * object.GetB();
}
private:
double& result_;
};
int main()
{
std::vector< Object > collection;
collection.push_back( Object( 1, 2 ) );
collection.push_back( Object( 3, 4 ) );
double result = 0.0;
std::for_each( collection.begin(), collection.end(),
Calculator( result ) );
std::cout << "result = " << result << std::endl;
return 0;
}
do changes in Calculator and main function.
struct Calculator
{
double operator() ( double result, const Object& obj )
{
return result + ( obj.GetA() * obj.GetB());
}
};
int main()
{
std::vector< Object > collection;
collection.push_back( Object( 1, 2 ) );
collection.push_back( Object( 3, 4 ) );
double result = std::accumulate( collection.begin(), collection.end(), 0, Calculator() );
std::cout << "result = " << result << std::endl;
return 0;
}
also it could be better:
double sumABProduct( double result, const Object& obj )
{
return result + ( obj.GetA() * obj.GetB());
}
double result = std::accumulate( collection.begin(), collection.end(), 0, sumABProduct );
Update 2: Boost.Lambda makes this a piece of cake:
// headers
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
using namespace boost::lambda;
// ...
cout << accumulate(dv.begin(), dv.end(),
0,
_1 += bind(&strange::value, _2)) //strange defined below
<< endl;
Update: This has been bugging me for a while. I can't just get any of the STL algorithms to work in a decent manner. So, I rolled my own:
// include whatever ...
using namespace std;
// custom accumulator that computes a result of the
// form: result += object.method();
// all other members same as that of std::accumulate
template <class I, class V, class Fn1, class Fn2>
V accumulate2(I first, I last, V val, Fn1 op, Fn2 memfn) {
for (; first != last; ++first)
val = op(val, memfn(*first));
return val;
}
struct strange {
strange(int a, int b) : _a(a), _b(b) {}
int value() { return _a + 10 * _b; }
int _a, _b;
};
int main() {
std::vector<strange> dv;
dv.push_back(strange(1, 3));
dv.push_back(strange(4, 6));
dv.push_back(strange(20, -11));
cout << accumulate2(dv.begin(), dv.end(),
0, std::plus<int>(),
mem_fun_ref(&strange::value)) << endl;
}
Of course, the original solution still holds:
The easiest is to implement an operator+. In this case:
double operator+(double v, Object const& x) {
return v + x.a_;
}
and make it a friend of Object or member (look up why you may prefer one over the other):
class Object
{
//...
friend double operator+(double v, Object const& x);
and you're done with:
result = accumulate(collection.begin(), collection.end(), 0.0);
My earlier approach doesn't work because we need a binary_function.
std::accumulate manual.
here is an issue here, I guess the arguments are written in the wrong order should be:
result = std::accumulate(collection.begin(), collection.end(), Object(0),Adapt())
where Adapt is defined thus:
struct Adapt {
static double mul(Object const &x) { return x.GetA() * x.GetB(); }
static Object operator()(Object const &x, Object const &y) {
return Object(mul(x)+mul(y)) ; } };
in this case of accumulate, the result is contained in a returned Object.
If you are using gnu parallel mode the functor will give you problems if the result and the actual object referred to by the iterator are different.
struct Adapt {
static double mul(Object const &x) { return x.GetA() * x.GetB(); }
static double operator()(Object const &x, Object const &y) {
return mul(x)+mul(y) ; } };
result = std::accumulate(collection.begin(), collection.end(), 0.0,Adapt())
will not work with gnu parallel mode for some strange and silly reason.
Using c++0x:
#include <numeric>
#include <vector>
#include <iostream>
class Object
{
public:
Object( double a, double b ):
a_( a ),
b_( b )
{}
double GetA() const { return a_; }
double GetB() const { return b_; }
// other methods
private:
double a_;
double b_;
};
int main()
{
std::vector< Object > collection;
collection.push_back( Object( 1, 2 ) );
collection.push_back( Object( 3, 4 ) );
double result = std::accumulate( collection.begin(), collection.end(), 0,
[] (double result, const Object& obj)
{
return result + obj.GetA() * obj.GetB();
}
);
std::cout << "result = " << result << std::endl;
return 0;
}
One would hope this is homework...
struct Adapt {
static double mul(Object const &x) { return x.GetA() * x.GetB(); }
static double operator()(Object const &x, Object const &y) {
return mul(x)+mul(y); } };
and
result = std::accumulate(collection.begin(), collection.end(), Object(0,0),Adapt() );
assuming you're not allowed to touch the declaration of Object.
Related
I am trying to use templates to represent simple polynomials like x^2 + 3x + 5. My idea is to represent them as sum of terms with each term having a coefficient and a power so e.g. x^2 has coeff=1 and power=2. I also want to be able to evaluate the polynomials for some x (they only have 1 unknown but in many places). So far I have:
struct PolyEnd{
double eval(double x){
return 0;
}
};
template <int coeff, int power, class Tail> struct Poly {
typedef Tail tt;
double eval(double x){
double curr = coeff * std::pow(x, power);
return curr; // has to call eval(x) on rest of the terms which are in the tail and return the sum with "curr"
}
};
int main()
{
double x = 2;
Poly<1,1,Poly<1,1,PolyEnd>> poly;
std::cout << poly.eval(x) << std::endl;
return 0;
}
However, I am stuck. Is what I am trying even possible? If so how can I make the recursive eval() calls work?
Yes, you can do that, you just need to call eval on the tail and since all the classes are state-less, you can just create an instance to call the member function on, on the spot:
struct PolyEnd{
double eval(double x){
return 0;
}
};
template <int coeff, int power, class Tail> struct Poly {
typedef Tail tt;
double eval(double x){
double curr = coeff * std::pow(x, power);
return curr + Tail{}.eval(x);
}
};
int main()
{
double x = 2;
Poly<1,1,Poly<1,1,PolyEnd>> poly;
std::cout << poly.eval(x) << std::endl;
return 0;
}
or if you make eval static, then you can call Tail::eval(x) directly.
I guess that you are experimenting with metaprogramming. Your question also made me excited, because i am also newbie in metaprogramming and I want to practise. #walnut's answer already accepted but there is no harm to share another implementation. I used some basic metaprogramming techniques.
I hope it will help you.
#include <cmath>
#include <iostream>
#include <string>
template<int Value>
struct coeff
{ };
template<int Value>
struct power
{ };
template<typename Coefficient, typename Power>
struct term;
template<int Coefficient , int Power>
struct term< coeff<Coefficient> , power<Power> >
{
inline double eval( double x ) const noexcept {
return Coefficient * std::pow( x , Power );
}
};
template<int Value>
using constant = term< coeff<Value> , power<1> >;
template<int Value>
using exponential = term< coeff<1> , power<Value> >;
template<typename... T>
struct polynomial
{
static_assert( sizeof...(T) == 0, "A polynomial can only be expressed in 'term's.");
[[nodiscard]] constexpr double eval( double ) const noexcept {
return 0;
}
[[nodiscard]] std::string to_string() const noexcept {
return std::string{};
}
};
template<int Coefficient, int Power, typename... Tail>
struct polynomial<term< coeff<Coefficient> , power<Power> >, Tail...>
: polynomial<Tail...>
{
[[nodiscard]] constexpr double eval( double x ) const noexcept {
return m_t.eval( x ) + polynomial<Tail...>::eval( x );
}
[[nodiscard]] std::string to_string(){
using namespace std;
using namespace std::string_literals;
return "("s + std::to_string( Coefficient ) +
string { "x^" } +
std::to_string( Power ) + ( sizeof...(Tail) == 0 ? ")" : ") + " ) +
polynomial<Tail...>::to_string();
}
private:
term< coeff<Coefficient> , power<Power> > m_t;
};
int main()
{
auto p1 = polynomial<term< coeff<1> , power<2> > ,
term< coeff<2> , power<4> > ,
term< coeff<2> , power<3> > ,
constant<3> ,
exponential<2> >{};
std::cout << "Polynomial is : " << p1.to_string() << std::endl;
std::cout << "f(2) : " << p1.eval( 2 ) << std::endl;
std::cout << "f(3) : " << p1.eval( 3 ) << std::endl;
return 0;
}
run online
Polynomial coefficients can be stored in std::array or std::vector(in case you define polynomial degree in runtime).
Then extend functionality with eval function.
template <unsigned N>
class Poly : public std::array<double, N> {
public:
template <typename... E>
Poly(E &&... e) : std::array<double, N>{{std::forward<E>(e)...}} {}
double eval(double x) {
double result = 0;
double exp = 1.;
for (auto it = this->rbegin(); it != this->rend(); ++it) {
result += exp * (*it);
exp *= x;
}
return result;
}
};
usage
double result = Poly<3>{3., 2., 1.}.eval(17);
How do I do the following with std::cout?
double my_double = 42.0;
char str[12];
printf_s("%11.6lf", my_double); // Prints " 42.000000"
I am just about ready to give up and use sprintf_s.
More generally, where can I find a reference on std::ostream formatting that lists everything in one place, rather than spreading it all out in a long tutorial?
EDIT Dec 21, 2017 - See my answer below. It uses features that were not available when I asked this question in 2012.
std::cout << std::fixed << std::setw(11) << std::setprecision(6) << my_double;
You need to add
#include <iomanip>
You need stream manipulators
You may "fill" the empty places with whatever char you want. Like this:
std::cout << std::fixed << std::setw(11) << std::setprecision(6)
<< std::setfill('0') << my_double;
std::cout << boost::format("%11.6f") % my_double;
You have to #include <boost\format.hpp>
In C++20 you can to do
double my_double = 42.0;
char str[12];
std::format_to_n(str, sizeof(str), "{:11.6}", my_double);
or
std::string s = std::format("{:11.6}", my_double);
In pre-C++20 you can use the {fmt} library that provides an implementation of format_to_n.
Disclaimer: I'm the author of {fmt} and C++20 std::format.
In general, you want to avoid specifying things like 11 and 6 at the
point of output. That's physical markup, and you want logical markup;
e.g. pressure, or volume. That way, you define in a single place
how pressure or volume are formatted, and if that formatting changes,
you don't have to search through out the program to find where to change
the format (and accidentally change the format of something else). In
C++, you do this by defining a manipulator, which sets the various
formatting options, and preferrably restores them at the end of the full
expression. So you end up writing things like:
std::cout << pressure << my_double;
Although I definitly wouldn't use it in production code, I've found the
following FFmt formatter useful for quicky jobs:
class FFmt : public StateSavingManip
{
public:
explicit FFmt(
int width,
int prec = 6,
std::ios::fmtflags additionalFlags
= static_cast<std::ios::fmtflags>(),
char fill = ' ' );
protected:
virtual void setState( std::ios& targetStream ) const;
private:
int myWidth;
int myPrec;
std::ios::fmtflags myFlags;
char myFill;
};
FFmt::FFmt(
int width,
int prec,
std::ios::fmtflags additionalFlags,
char fill )
: myWidth( width )
, myPrec( prec )
, myFlags( additionalFlags )
, myFill( fill )
{
myFlags &= ~ std::ios::floatfield
myFlags |= std::ios::fixed
if ( isdigit( static_cast< unsigned char >( fill ) )
&& (myFlags & std::ios::adjustfield) == 0 ) {
myFlags |= std::ios::internal
}
}
void
FFmt::setState(
std::ios& targetStream ) const
{
targetStream.flags( myFlags )
targetStream.width( myWidth )
targetStream.precision( myPrec )
targetStream.fill( myFill )
}
This allows writing things like:
std::cout << FFmt( 11, 6 ) << my_double;
And for the record:
class StateSavingManip
{
public:
StateSavingManip(
StateSavingManip const& other );
virtual ~StateSavingManip();
void operator()( std::ios& stream ) const;
protected:
StateSavingManip();
private:
virtual void setState( std::ios& stream ) const = 0;
private:
StateSavingManip& operator=( StateSavingManip const& );
private:
mutable std::ios* myStream;
mutable std::ios::fmtflags
mySavedFlags;
mutable int mySavedPrec;
mutable char mySavedFill;
};
inline std::ostream&
operator<<(
std::ostream& out,
StateSavingManip const&
manip )
{
manip( out );
return out;
}
inline std::istream&
operator>>(
std::istream& in,
StateSavingManip const&
manip )
{
manip( in );
return in;
}
StateSavingManip.cc:
namespace {
// We maintain the value returned by ios::xalloc() + 1, and not
// the value itself. The actual value may be zero, and we need
// to be able to distinguish it from the 0 resulting from 0
// initialization. The function getXAlloc() returns this value
// -1, so we add one in the initialization.
int getXAlloc();
int ourXAlloc = getXAlloc() + 1;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1;
assert( ourXAlloc != 0 );
}
return ourXAlloc - 1;
}
}
StateSavingManip::StateSavingManip()
: myStream( NULL )
{
}
StateSavingManip::StateSavingManip(
StateSavingManip const&
other )
{
assert( other.myStream == NULL );
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != NULL ) {
myStream->flags( mySavedFlags );
myStream->precision( mySavedPrec );
myStream->fill( mySavedFill );
myStream->pword( getXAlloc() ) = NULL;
}
}
void
StateSavingManip::operator()(
std::ios& stream ) const
{
void*& backptr = stream.pword( getXAlloc() );
if ( backptr == NULL ) {
backptr = const_cast< StateSavingManip* >( this );
myStream = &stream;
mySavedFlags = stream.flags();
mySavedPrec = stream.precision();
mySavedFill = stream.fill();
}
setState( stream );
}
#include <iostream>
#include <iomanip>
int main() {
double my_double = 42.0;
std::cout << std::fixed << std::setw(11)
<< std::setprecision(6) << my_double << std::endl;
return 0;
}
For future visitors who prefer actual printf-style format specs with std::ostream, here is yet another variation, based on Martin York's excellent post in another SO question: https://stackoverflow.com/a/535636:
#include <iostream>
#include <iomanip>
#include <stdio.h> //snprintf
class FMT
{
public:
explicit FMT(const char* fmt): m_fmt(fmt) {}
private:
class fmter //actual worker class
{
public:
explicit fmter(std::ostream& strm, const FMT& fmt): m_strm(strm), m_fmt(fmt.m_fmt) {}
//output next object (any type) to stream:
template<typename TYPE>
std::ostream& operator<<(const TYPE& value)
{
// return m_strm << "FMT(" << m_fmt << "," << value << ")";
char buf[40]; //enlarge as needed
snprintf(buf, sizeof(buf), m_fmt, value);
return m_strm << buf;
}
private:
std::ostream& m_strm;
const char* m_fmt;
};
const char* m_fmt; //save fmt string for inner class
//kludge: return derived stream to allow operator overloading:
friend FMT::fmter operator<<(std::ostream& strm, const FMT& fmt)
{
return FMT::fmter(strm, fmt);
}
};
usage example:
double my_double = 42.0;
cout << FMT("%11.6f") << my_double << "more stuff\n";
or even:
int val = 42;
cout << val << " in hex is " << FMT(" 0x%x") << val << "\n";
it's me, the OP, Jive Dadson - five years on. C++17 is becoming a reality.
The advent of variadic template parameters with perfect forwarding has made life so much simpler. The chained madness of ostream<< and boost::format% can be dispensed with. The function oprintf below fills the bill. Work in progress. Feel free to chime in on error-handling, etc...
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <string_view>
namespace dj {
template<class Out, class... Args>
Out& oprintf(Out &out, const std::string_view &fmt, Args&&... args) {
const int sz = 512;
char buffer[sz];
int cx = snprintf(buffer, sz, fmt.data(), std::forward<Args>(args)...);
if (cx >= 0 && cx < sz) {
return out.write(buffer, cx);
} else if (cx > 0) {
// Big output
std::string buff2;
buff2.resize(cx + 1);
snprintf(buff2.data(), cx, fmt.data(), std::forward<Args>(args)...);
return out.write(buff2.data(), cx);
} else {
// Throw?
return out;
}
}
}
int main() {
const double my_double = 42.0;
dj::oprintf(std::cout, "%s %11.6lf\n", "My double ", my_double);
return 0;
}
Some great answers already; kudos to those!
This is based on some of them. I have added type assertions for POD types, since they are the only safe types usable with printf().
#include <iostream>
#include <stdio.h>
#include <type_traits>
namespace fmt {
namespace detail {
template<typename T>
struct printf_impl
{
const char* fmt;
const T v;
printf_impl(const char* fmt, const T& v) : fmt(fmt), v(v) {}
};
template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, std::ostream& >::type
operator<<(std::ostream& os, const printf_impl<T>& p)
{
char buf[40];
::snprintf(buf, sizeof(buf), p.fmt, p.v, 40);
return os << buf;
}
} // namespace detail
template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, detail::printf_impl<T> >::type
printf(const char* fmt, const T& v)
{
return detail::printf_impl<T>(fmt, v);
}
} // namespace fmt
Example usage it as below.
std::cout << fmt::printf("%11.6f", my_double);
Give it a try on Coliru.
I am working on a physics problem for which I have to evolve parameters according to ODEs. From time to time they have to manipulated so that I would like to have a data type that can be used with routines such as diagonalisation,... Therefore, I implemented a class with eigen::Matrix as members and want to perform the integration with odeint. For a single eigen::matrix this worked fine. I made a minimal example:
#include <iostream>
#include <fstream>
#include <cmath>
#include <string>
#include <Eigen/Core>
#include <boost/numeric/odeint.hpp>
#include <boost/numeric/odeint/external/eigen/eigen_algebra.hpp>
// define vector_space_algebra for Eigen::Matrix
namespace boost::numeric::odeint {
template<typename B,int S1,int S2,int O, int M1, int M2>
struct algebra_dispatcher< Eigen::Matrix<B,S1,S2,O,M1,M2> >{
typedef vector_space_algebra algebra_type;
};
}
// define abs() for Eigen::Matrix
namespace Eigen {
template<typename D, int Rows, int Cols>
Matrix<D, Rows, Cols> abs(Matrix<D, Rows, Cols> const& m) {
return m.cwiseAbs();
}
}
typedef Eigen::Matrix<double, 3,3> mat;
using namespace Eigen;
using namespace std;
class state {
public:
// state components
Eigen::Matrix<double, 3,3> M1, M2;
// constructors
state() : M1(), M2() {}; // constructors
state(mat M1in, mat M2in) : M1(M1in), M2(M2in) {};
// in place addition and multiplication
state operator+=(const state & X){
M1 += X.M1; M2 += X.M2;
return *this;
}
state operator*=(const double a){
M1 *= a; M2 *= a;
return *this;
}
// ODE
void operator() ( const state & X , state & dX, const double ){
dX.M1 = X.M1*X.M2.adjoint()*X.M2;
dX.M2 = X.M2*X.M1.adjoint()*X.M1;
}
};
// vector space operations
state operator+( const state &lhs , const state &rhs ){
return state( lhs.M1+rhs.M1 ,lhs.M2+rhs.M2);
}
state operator*( const state &lhs , const double &rhs ){
return state( lhs.M1*rhs ,lhs.M2*rhs);
}
state operator*( const double &lhs , const state &rhs ){
return state( lhs*rhs.M1 ,lhs*rhs.M2);
}
state operator/( const state &lhs , const state &rhs ){
return state( lhs.M1.cwiseQuotient(rhs.M1), lhs.M2.cwiseQuotient(rhs.M2) );
}
state abs( const state &X ){
return state( abs(X.M1) , abs(X.M2) );
}
// lp infinity norm
namespace boost::numeric::odeint {
template<>
struct vector_space_norm_inf< state > {
typedef double result_type;
double operator()( const state &X ) const {
return max( X.M1.lpNorm<Infinity>() , X.M2.lpNorm<Infinity>() );
}
};
}
//write to std output
void write( state &x , const double t ){
cout << t << "\t" << x.M1 << "\t" << x.M2 << "\n";
}
//
// int main
//
int main(int argc, char* argv[]){
// set values
mat M1, M2;
double t_end = 1;
double t_start = 10;
M1 << 0.1,0,0, 0,0.2,0.1 ,0.2,0,0.3;
M2 << 0.5,0,0, 0,0.6,0, 0,0,0.7;
state values(M1,M2);
using namespace boost::numeric::odeint;
// type definition for numerical integration
typedef runge_kutta_dopri5< state , double, state , double, vector_space_algebra > stepper;
// integration
int steps = integrate_adaptive( make_controlled<stepper>( 1E-10 , 1E-10 ) , state() , values , t_start , t_end , 0.01);
//output
write(values,t_end);
return(0);
}
Basically, this is the example taken from here
When I comment out the line starting with "int steps", g++ on mac (I know, thats something different, what the errors are way more readable...) compiles without errors. Otherwise, I get
In file included from minimal.cpp:7:
In file included from /usr/local/include/boost/numeric/odeint.hpp:25:
In file included from /usr/local/include/boost/numeric/odeint/util/ublas_wrapper.hpp:30:
/usr/local/include/boost/numeric/odeint/algebra/default_operations.hpp:443:76: error:
invalid operands to binary expression ('double' and 'state')
...m_eps_abs + m_eps_rel * ( m_a_x * abs( get_unit_value( t1 ) ) + m_a_dxdt * abs( get_unit_value( t2 ) ) )...
Since it is not declared in that file, I did not understand, what the function get_unit_value() does or wants from me. It seems to have something to do with the error estimation or at least with performing a certain step in the integration. How can I fix it?
The issue is that you did not define the operator+ between double and state. Add the following, and the code should at least compile.
state operator+( const state &lhs , double rhs ){
return state( lhs.M1+rhs ,lhs.M2+rhs);
}
state operator+( double lhs , const state &rhs ){
return state( lhs+rhs.M1 ,lhs+rhs.M2);
}
If I compile the code below, I get an:
microsoft visual studio 12.0\vc\include\xrefwrap(58): error C2064: term does not evaluate to a function taking 2 arguments
In the call to accumulate algorithm, if I change the code to function<double(double, Position const&) > f = bind(sum, placeholders::_1, bind(mem_fn(&Position::getBalance), placeholders::_2));double sum = accumulate(balances.begin(), balances.end(), 0., f); everything compiles fine. I also tried to use a non member function, but it doesn't work neither.
class Position
{
private:
double m_balance;
public:
Position(double balance) :
m_balance(balance)
{}
double getBalance() const
{
return m_balance;
}
};
static double sum(double v1, double v2)
{
return v1 + v2;
}
int main(int argc, char ** argv)
{
std::vector< Position > balances;
for (size_t i = 0; i < 10; i++)
{
balances.push_back(Position(i));
}
double sum = accumulate(balances.begin(), balances.end(), 0., bind(sum, placeholders::_1, bind(mem_fn(&Position::getBalance), placeholders::_2)));
cout << sum << endl;
return 0;
}
This will fix it:
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
std::bind(std::plus<>(),
placeholders::_1,
std::bind(&Position::getBalance, placeholders::_2)));
or we could be kind to our fellow programmers:
auto add_balance = [](auto x, auto& position) {
return x + position.getBalance();
};
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
add_balance);
Or of course we can inline the lambda. There's no performance difference. Which one seems clearer will be a matter of personal preference.
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
[](auto x, auto& position)
{
return x + position.getBalance();
});
Or we can write a complex functor to do a similar job. This was the pre-lambda way:
template<class Op>
struct PositionOp
{
using mfp_type = double (Position::*)() const;
PositionOp(Op op, mfp_type mfp) : op(op), mfp(mfp) {}
template<class T>
auto operator()(T x, const Position& pos) const {
return op(x, (pos.*mfp)());
}
Op op;
mfp_type mfp;
};
template<class Op>
auto position_op(Op op, double (Position::*mfp)() const)
{
return PositionOp<Op>(op, mfp);
}
...
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
position_op(std::plus<>(), &Position::getBalance));
... but I hope you'll agree that this is ghastly.
Background
Large application with a bundle of code, I can't change the storage mechanism.
I would like to create an iterator over a set of multi-dimensional data stored in parallel arrays so we can start using std algorithms & containers.
Any ideas on how to make this work correctly?
#include <boost/iterator/iterator_facade.hpp>
#include <iostream>
#include <algorithm>
class curve_point_iterator;
const int curve_size = 10;
class curve
{
public:
curve()
{
std::fill( x, &x[curve_size], 0.0 );
std::fill( y, &y[curve_size], 0.0 );
}
double x[curve_size];
double y[curve_size];
curve_point_iterator begin();
curve_point_iterator end();
};
class point_reference
{
public:
point_reference( double& x_, double& y_ )
: x( x_ )
, y( y_ )
{
}
point_reference& operator = ( point_reference& other )
{
x = other.x;
y = other.y;
return *this;
}
double & x;
double & y;
};
class curve_point_iterator
: public boost::iterator_facade<
curve_point_iterator
, point_reference
, boost::random_access_traversal_tag >
{
public:
curve_point_iterator()
: index(0)
, curve_(nullptr)
{}
explicit curve_point_iterator( curve* curve_in, size_t index_ = 0 )
: index( index_ )
, curve_( curve_in )
{}
private:
friend class boost::iterator_core_access;
void increment()
{
++index;
}
void decrement()
{
--index;
}
void advance( size_t n )
{
index += n;
}
difference_type distance_to( curve_point_iterator const& other ) const
{
return other.index - this->index;
}
bool equal(curve_point_iterator const& other) const
{
return this->index == other.index && this->curve_ == other.curve_;
}
point_reference& dereference() const
{
auto pt_ref = new( point_reference_buffer ) point_reference( curve_->x[index]
, curve_->y[index] );
return *pt_ref;
}
size_t index;
mutable char point_reference_buffer[sizeof(point_reference)];
curve* curve_;
};
curve_point_iterator curve::begin()
{
return curve_point_iterator( this );
}
curve_point_iterator curve::end()
{
return curve_point_iterator( this, curve_size+1 );
}
int main(int argc, char* argv[])
{
curve crv;
crv.x[1] = 20;
crv.x[2] = 10;
std::sort( crv.begin(), crv.end(), []( point_reference const& a, point_reference const& b )
{
return a.x < b.x;
});
for( auto i = 0; i < curve_size; ++i )
{
std::cout << crv.x[i] << std::endl;
}
return 0;
}
Output
0
20
20
20
20
20
20
20
20
20
After changing to
class point_reference
{
... ( all the other stuff )
double x; // no longer reference
double y; // no longer reference
};
Output
0
20
10
0
0
0
0
0
0
Okay, what you need to do is to introduce a point type (which has value semantics) in addition to point_reference, which has the reference semantics you're looking for. You need the value semantics so that operations such as swap act as the standard expects. You can use the fourth argument of iterator_facade to allow this. Also, this way there is no need to use that mutable buffer, since the point_reference itself is returned by value.
Also, your curve::end() used the wrong index, and should use curve_size as its index.
#include <boost/iterator/iterator_facade.hpp>
#include <iostream>
#include <algorithm>
class curve_point_iterator;
const int curve_size = 10;
class curve
{
public:
curve()
{
std::fill( x, &x[curve_size], 0.0 );
std::fill( y, &y[curve_size], 0.0 );
}
double x[curve_size];
double y[curve_size];
curve_point_iterator begin();
curve_point_iterator end();
};
class point
{
public:
point( const double& x_, const double& y_ )
: x( x_ )
, y( y_ )
{
}
double x;
double y;
};
class point_reference
{
public:
point_reference( double& x_, double& y_ )
: x( x_ ),
y( y_ )
{
}
point_reference& operator = ( const point& other )
{
x = other.x;
y = other.y;
return *this;
}
operator point() const
{
return point(x, y);
}
double & x;
double & y;
point_reference& operator=(const point_reference& other)
{
x = other.x;
y = other.y;
}
point_reference* operator->()
{
return this;
}
point_reference* operator->() const
{
return this;
}
};
class curve_point_iterator
: public boost::iterator_facade<
curve_point_iterator
, point
, boost::random_access_traversal_tag
, point_reference>
{
public:
curve_point_iterator()
: index(0)
, curve_(nullptr)
{}
explicit curve_point_iterator( curve* curve_in, size_t index_ = 0 )
: index( index_ )
, curve_( curve_in )
{}
point_reference operator->() const
{
return dereference();
}
private:
friend class boost::iterator_core_access;
void increment()
{
++index;
}
void decrement()
{
--index;
}
void advance( size_t n )
{
index += n;
}
difference_type distance_to( curve_point_iterator const& other ) const
{
return other.index - this->index;
}
bool equal(curve_point_iterator const& other) const
{
return this->index == other.index && this->curve_ == other.curve_;
}
point_reference dereference() const
{
// auto pt_ref = new( point_reference_buffer ) point_reference( curve_->x[index]
// , curve_->y[index] );
// return *pt_ref;
return point_reference(curve_->x[index], curve_->y[index]);
}
size_t index;
curve* curve_;
};
curve_point_iterator curve::begin()
{
return curve_point_iterator( this );
}
curve_point_iterator curve::end()
{
return curve_point_iterator( this, curve_size );
}
int main(int argc, char* argv[])
{
curve crv;
crv.x[1] = 20;
crv.x[2] = 10;
std::sort( crv.begin(), crv.end(), []( point const& a, point const& b )
{
return a.x < b.x;
});
for( auto i = 0; i < curve_size; ++i )
{
std::cout << crv.x[i] << std::endl;
}
return 0;
}
Output:
0
0
0
0
0
0
0
0
10
20