Writing an random access iterator over parallel arrays - c++

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

Related

C++ keep force driven entities in the window

I have a window application of 1024 width and 768 height and contains a bunch of meteorites, ships and boats. The meteorites freely roam across the window driven by forces.
The forces are: random position, towards/away from boat, towards/away from ship and cohesion, seperation, alignment to other meteorites
I feel like the forces are not fully working since they sometimes move off the screen and move with inverted velocity eg: they are roaming from top right straight to top left and when reached go straight to bottom left.
Are my calculations correct or did I mess up something at the forces?
Meteorite header:
#include <chrono>
#include <cmath>
#include <array>
#include <random>
#include <algorithm>
using scalar = float;
template <typename Scalar> class basic_vector2d {
public:
constexpr basic_vector2d() noexcept = default;
constexpr basic_vector2d(Scalar x, Scalar y) noexcept : x_{ x }, y_{ y } {}
constexpr Scalar x() const noexcept { return x_; }
constexpr void x(Scalar newX) noexcept { x_ = newX; }
constexpr Scalar y() const noexcept { return y_; }
constexpr void y(Scalar newY) noexcept { y_ = newY; }
constexpr bool operator==(basic_vector2d other) const noexcept {
return x_ == other.x_ && y_ == other.y_;
}
constexpr bool operator!=(basic_vector2d other) const noexcept {
return x_ != other.x_ || y_ != other.y_;
}
constexpr basic_vector2d& operator+=(basic_vector2d other) noexcept {
x_ += other.x_;
y_ += other.y_;
return *this;
}
constexpr basic_vector2d& operator-=(basic_vector2d other) noexcept {
x_ -= other.x_;
y_ -= other.y_;
return *this;
}
constexpr basic_vector2d& operator*=(Scalar s) noexcept {
x_ *= s;
y_ *= s;
return *this;
}
constexpr basic_vector2d& operator/=(Scalar s) noexcept {
x_ /= s;
y_ /= s;
return *this;
}
private:
Scalar x_{};
Scalar y_{};
};
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator-(basic_vector2d<Scalar> a,
basic_vector2d<Scalar> b) {
return { a.x() - b.x(), a.y() - b.y() };
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator+(basic_vector2d<Scalar> a,
basic_vector2d<Scalar> b) {
return { a.x() + b.x(), a.y() + b.y() };
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator*(basic_vector2d<Scalar> v, scalar s) {
return v *= s;
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator*(scalar s, basic_vector2d<Scalar> v) {
return operator*(v, s);
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator/(basic_vector2d<Scalar> v, scalar s) {
return v /= s;
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator/(scalar s, basic_vector2d<Scalar> v) {
return operator/(v, s);
}
template <typename Scalar>
constexpr scalar dot(basic_vector2d<Scalar> a, basic_vector2d<Scalar> b) {
return a.x() * b.x() + a.y() * b.y();
}
template <typename Scalar> constexpr auto norm(basic_vector2d<Scalar> p) {
return std::sqrt(dot(p, p));
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> normalize(basic_vector2d<Scalar> p) {
auto ls = norm(p);
return { p.x() / ls, p.y() / ls };
}
using vector2d = basic_vector2d<scalar>;
template <typename T> class basic_size {
public:
constexpr basic_size() noexcept = default;
constexpr basic_size(T width, T height) noexcept
: width_{ width }, height_{ height } {}
constexpr T width() const noexcept { return width_; }
constexpr T height() const noexcept { return height_; }
constexpr void width(T new_width) noexcept { width_ = new_width; }
constexpr void height(T new_height) noexcept { height_ = new_height; }
constexpr basic_size& operator*=(T x) {
width(width() * x);
height(height() * x);
return *this;
}
private:
T width_{};
T height_{};
};
using size = basic_size<scalar>;
template <typename Scalar> class basic_rectangle {
public:
constexpr basic_rectangle(basic_vector2d<Scalar> top_left,
basic_size<Scalar> size)
: top_left_{ top_left }, size_{ size } {}
constexpr basic_vector2d<Scalar> const& top_left() const noexcept {
return top_left_;
}
constexpr basic_size<Scalar> const& size() const noexcept { return size_; }
private:
basic_vector2d<Scalar> top_left_;
basic_size<Scalar> size_;
};
using rectangle = basic_rectangle<scalar>;
inline float to_seconds(std::chrono::nanoseconds dt) {
return std::chrono::duration_cast<std::chrono::duration<float>>(dt).count();
}
class meteorite {
public:
meteorite(int id, vector2d location);
int id;
/*!
* Called every tick
* \param dt the time that has passed since the previous tick
*/
void act(std::chrono::nanoseconds dt);
vector2d location() const { return location_; }
std::vector<vector2d> random_meteorite_locations(std::size_t n);
private:
vector2d velocity;
scalar max_velocity;
vector2d location_;
vector2d acceleration;
void location(vector2d loc) { location_ = loc; }
void random_position_force();
void screen_force(std::chrono::nanoseconds dt);
void move(std::chrono::nanoseconds dt);
void add_force(vector2d force);
void island_avoidance();
};
Meteorite source:
#include "meteorite.h"
meteorite::meteorite(int id, vector2d location) : id(id), velocity{ 0, 0 }, max_velocity(0.15), acceleration{ 0, 0 }, location_(location) {}
void meteorite::act(std::chrono::nanoseconds dt) {
move(dt);
}
void meteorite::move(std::chrono::nanoseconds dt) {
this->location(this->location() + velocity);
random_position_force();
screen_force(dt);
this->velocity += this->acceleration * to_seconds(dt);
// prevent velocity from exceeding max_velocity
float velocity_length = std::sqrt((this->velocity.x() * this->velocity.x()) + (this->velocity.y() * this->velocity.y()));
if (velocity_length >= this->max_velocity) {
this->velocity = normalize(this->velocity) * this->max_velocity;
}
/*directions:
* y -1 up
* y 1 down
*
* x 1 right
* x -1 left
*/
// reset acceleration to 0 for the next set of forces to be applied
this->acceleration = vector2d(0, 0);
}
// add force propeling meteorite to a random position
void meteorite::random_position_force() {
float x = (rand() % 100 - 50);
float y = (rand() % 100 - 50);
add_force(this->velocity + vector2d((x / 5), (y / 5)));
}
void meteorite::add_force(vector2d force) {
this->acceleration += force;
}
void meteorite::screen_force(std::chrono::nanoseconds dt)
{
auto new_position = this->location() + (this->velocity + (this->acceleration * to_seconds(dt)));
auto height = 1068 - 32;
auto width = 724 - 32;
if (new_position.x() <= 32) {
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.x() < 0)
{
screen_vector = vector2d(-this->acceleration.x() * 2, 0);
}
add_force(screen_vector);
}
else if (new_position.x() >= width)
{
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.x() > 0)
{
screen_vector = vector2d(-this->acceleration.x() * 2, 0);
}
add_force(screen_vector);
}
if (new_position.y() <= 32) {
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.y() < 0)
{
screen_vector = vector2d(0, -this->acceleration.y() * 2);
}
add_force(screen_vector);
}
else if (new_position.y() >= height)
{
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.y() > 0)
{
screen_vector = vector2d(0, -this->acceleration.y() * 2);
}
add_force(screen_vector);
}
}
std::vector<vector2d> meteorite::random_meteorite_locations(std::size_t n) {
// from 0x2 to 13x17 = 195
// from 13x0 to 28x9 = 135
// from 20x9 to 32x19 = 120
// from 6x17 to 25x24 = 133
// sum = 583
std::random_device rd{};
std::default_random_engine re{ rd() };
std::uniform_int_distribution<> id{ 0, 583 };
std::uniform_real_distribution<scalar> sd{ 0, 1 };
auto rv = [&](rectangle const& r) {
return r.top_left() + vector2d{ r.size().width() * sd(re),
r.size().height() * sd(re) };
};
std::array<rectangle, 4> rects{
rectangle{vector2d{0.1f, 2}, size{13, 15}},
rectangle{vector2d{13.f, 0.1f}, size{15, 9}},
rectangle{vector2d{20, 9}, size{12, 10}},
rectangle{vector2d{6, 17}, size{17, 6}} };
auto to_index = [](int i) -> std::size_t {
if (i < 195)
return 0;
else if (i < 330)
return 1;
else if (i < 450)
return 2;
else
return 3;
};
std::vector<vector2d> result(n);
std::generate_n(result.begin(), result.size(), [&] {
auto val = id(re);
auto index = to_index(val);
auto rect = rects[index];
return 32 * rv(rect);
});
return result;
}
Main.cpp
#include <iostream>
#include "meteorite.h"
int main()
{
meteorite m = meteorite{ 0, {} };
std::vector<meteorite*> meteorites;
std::vector<vector2d> locations = m.random_meteorite_locations(1);
int i = 1;
for (auto& loc : locations) {
meteorites.push_back(new meteorite(i, loc));
}
auto t_prev = std::chrono::high_resolution_clock::now();
while (true) {
auto t_current = std::chrono::high_resolution_clock::now();
std::chrono::nanoseconds dt = std::chrono::nanoseconds(200);
t_prev = t_current;
for (auto& m : meteorites) {
m->act(dt);
std::cout << m->location().x() << " " << m->location().y() << "\n";
}
}
for (auto& m : meteorites) {
delete m;
}
}
You're computing the new position incorrectly in both places, move() and screen_force(). You're doing s = s0 + (v + a * t), but you should be doing s = s0 + v * t + (a * t^2) / 2.
Here's a working example:
http://cpp.sh/9uu3w

requirements for custom container type to use with views

I start to play with std::ranges and want understand how views really work. So I try to write my own container and iterator type and want to use it in a view.
But something seems to be missing but the compiler only tells me that there is no begin() method inside the view but not why.
Example:
#include <iostream>
#include <array>
#include <ranges>
class MyFixedContainer;
class MyIterator
{
MyFixedContainer* ptr;
unsigned int offset;
public:
MyIterator( MyFixedContainer* ptr_, unsigned int offset_ ): ptr{ ptr_},offset{offset_}{}
bool operator==( MyIterator& other ) const
{
return ( ptr == other.ptr )&& ( offset == other.offset );
}
bool operator!=( MyIterator& other ) const
{
return !(*this == other);
}
MyIterator operator++()
{
offset++;
return *this;
}
MyIterator operator++(int)
{
MyIterator tmp = *this;
offset++;
return tmp;
}
int operator*() const;
};
class MyFixedContainer
{
std::array<int,4> arr={5,6,7,8};
public:
auto begin() { return MyIterator{ this, 0 }; }
auto end() { return MyIterator{ this, 4}; }
int Get( int offset ) const
{
return arr[ offset ];
}
};
int MyIterator::operator*() const
{
return ptr->Get( offset );
}
int main()
{
MyFixedContainer c;
// Container type itself works:
for ( int i: c )
{
std::cout << i << std::endl;
}
// Try to use with std::ranges
auto even = [] (int i) { return 0 == i % 2; };
auto y = std::views::filter(c, even);
auto b = y.begin(); // << error message
}
Compiles with
main.cpp:90:16: error: 'struct std::ranges::views::__adaptor::_RangeAdaptorClosurestd::ranges::views::__adaptor::_RangeAdaptor<_Callable::operator()<{MyFixedContainer&, main()::<lambda(int)>&}>::<lambda(_Range&&)> >' has no member named 'begin'
90 | auto b = y.begin();
https://godbolt.org/z/doW76j
MyIterator does not model std::input_or_output_iterator because:
It needs to be default constructible.
std::iter_difference_t<MyIterator> must be valid, and
the pre-increment operator must return a reference.
MyIterator is not a std::sentinel_for<MyIterator, MyIterator> because its operators == and != take references instead of const references.
MyIterator does not satisfy std::input_iterator, which requires std::iter_value_t to be valid.
Fixing all of the above:
#include <iostream>
#include <array>
#include <ranges>
class MyFixedContainer;
class MyIterator
{
MyFixedContainer* ptr;
unsigned int offset;
public:
using difference_type = int;
using value_type = int;
MyIterator() = default;
MyIterator( MyFixedContainer* ptr_, unsigned int offset_ ): ptr{ ptr_},offset{offset_}{}
bool operator==( MyIterator const & other ) const
{
return ( ptr == other.ptr )&& ( offset == other.offset );
}
bool operator!=( MyIterator const & other ) const
{
return !(*this == other);
}
MyIterator &operator++()
{
offset++;
return *this;
}
MyIterator operator++(int)
{
MyIterator tmp = *this;
offset++;
return tmp;
}
int operator*() const;
};
class MyFixedContainer
{
std::array<int,4> arr={5,6,7,8};
public:
auto begin() { return MyIterator{ this, 0 }; }
auto end() { return MyIterator{ this, 4}; }
int Get( int offset ) const
{
return arr[ offset ];
}
};
int MyIterator::operator*() const
{
return ptr->Get( offset );
}
int main()
{
MyFixedContainer c;
// Container type itself works:
for ( int i: c )
{
std::cout << i << std::endl;
}
// Try to use with std::ranges
auto even = [] (int i) { return 0 == i % 2; };
static_assert(std::input_or_output_iterator<MyIterator>);
static_assert(std::ranges::input_range<MyFixedContainer>);
auto y = c | std::views::filter(even);
auto b = y.begin(); // << OK
}
The error messages are much clearer if you static_assert every concept that your container/iterator has to model.

Why is the trimmed down shared_ptr version here slower than boost::shared_ptr?

I thought this is already very simple and not doing much (Took it from one of the stack overflow question answer code), but running a loop of 100K times shows it's slower than boost::shared_ptr. Baffled, where is the slowness come from?
template <class T>
class sp_shared_ptr
{
public:
inline sp_shared_ptr() noexcept: p_(), count_() {}
inline explicit sp_shared_ptr( T* p ) noexcept: p_( p ), count_( new int(1) ) {}
inline sp_shared_ptr( const sp_shared_ptr& sp ) noexcept: p_( sp.p_ ), count_( sp.count_ )
{
if( count_ ) ++(*count_);
}
// EDIT: add in later based on comments
sp_shared_ptr( sp_shared_ptr && r ) noexcept : p_( r.p_ ), count_()
{
int *tmp = r.count_;
r.count_ = count_;
count_ = tmp;
r.p_ = 0;
}
sp_shared_ptr& operator=( const sp_shared_ptr& sp ) noexcept
{
if( this != &sp )
{
clear();
p_ = sp.p_;
count_ = sp.count_;
if( count_ ) ++*count_;
}
return *this;
}
~sp_shared_ptr() noexcept
{
clear();
}
private:
inline void clear() noexcept
{
if( count_ )
{
--*count_;
if( *count_ == 0 ) {
delete p_; p_=0;
delete count_; count_=0;
}
}
}
T* get() const { return count_ ? p_ : 0; }
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
T* p_;
int* count_;
};
The output of the following program is:
10229594
14577150
14030158
EDIT: Based on #BoPersson's comment, edit in the move constructor. Now, result is getting closer:
10201099
14121221
14040181
I wonder why it is faster, or quite a bit faster (No atomic counter didn't do any good in my test?)
Calling program:
#include <iostream>
#include <map>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/smart_ptr.hpp>
#include <time.h>
#include "sp_shared_ptr.h"
struct A
{
int a1;
double a2;
std::string a3;
};
long long duration( const timespec& t0, const timespec& t1 )
{
return (t1.tv_sec*1000000000L + t1.tv_nsec) - (t0.tv_sec*1000000000L + t0.tv_nsec);
}
int main()
{
typedef std::vector<std::unique_ptr<A>> UPVector;
typedef std::vector<boost::shared_ptr<A>> SPVector;
typedef std::vector<sp_shared_ptr<A>> MySPVector;
UPVector v1;
SPVector v2;
MySPVector v3;
const int n=100000;
timespec t0, t1;
clock_gettime( CLOCK_REALTIME, &t0 );
clock_gettime( CLOCK_REALTIME, &t0 );
{
for( auto i=0; i < n; ++i )
v1.push_back( std::unique_ptr<A>( new A { i, i/100.0, "aaaaaaaaaa" } ) );
}
clock_gettime( CLOCK_REALTIME, &t1 );
std::cout << duration( t0, t1 ) << std::endl;
clock_gettime( CLOCK_REALTIME, &t0 );
{
for( auto i=0; i < n; ++i )
v3.push_back( sp_shared_ptr<A>( new A{ i, i/100.0, "aaaaaaaaaa" } ) );
}
clock_gettime( CLOCK_REALTIME, &t1 );
std::cout << duration( t0, t1 ) << std::endl;
clock_gettime( CLOCK_REALTIME, &t0 );
{
for( auto i=0; i < n; ++i )
v2.push_back( boost::shared_ptr<A>( new A{ i, i/100.0, "aaaaaaaaaa" } ) );
}
clock_gettime( CLOCK_REALTIME, &t1 );
std::cout << duration( t0, t1 ) << std::endl;
return 0;
}

overloading [][] operators in c++

I'm writing a matrix 3x3 class in c++.
glm::mat3 provides access to matrix data through the [][] operator syntax.
e.g. myMatrix[0][0] = 1.0f; would set the first row, first column entry to 1.0f.
I'd like to provide similar access. How can I overload the [][] operators?
I've tried the following, but I get errors:
operator name must be declared as a function
const real operator[][](int row, int col) const
{
// should really throw an exception for out of bounds indices
return ((row >= 0 && row <= 2) && (col >= 0 && col <= 2)) ? _data[row][col] : 0.0f;
}
What's the correct way to overload this operator?
There is no operator [][], so you need to overload the [] operator twice: once on the matrix, returning a surrogate object for the row, and once for the returned surrogate row:
// Matrix's operator[]
const row_proxy operator[](int row) const
{
return row_proxy(this, row);
}
// Proxy's operator[]
const real operator[](int col) const
{
// Proxy stores a pointer to matrix and the row passed into the first [] operator
return ((this->row >= 0 && this->row <= 2) && (col >= 0 && col <= 2)) ? this->matrix->_data[this->row][col] : 0.0f;
}
It would be easier to make the method double operator() (int row, int col) const. Instead of matrix[i][j] you just say matrix(i,j).
Generally, for multiple argument you want to use operator(), not operator[].
Hm, in case it wasn't obvious, there is no operator[][] in C++. It's just operator[] applied twice. Which means that if you want that notation, then you have to let the first one return a result (indexable thing or proxy) that the second can be applied to.
The code below sketches some approaches, choose what you like:
#include <iostream>
#include <vector>
template< int n >
int& dummy() { static int elem = n; return elem; }
struct Mat1
{
int operator() ( int const x, int const y ) const
{ return dummy<1>(); }
int& operator() ( int const x, int const y )
{ return dummy<1>(); }
Mat1( int, int ) {}
};
struct Mat2
{
int at( int const x, int const y ) const
{ return dummy<2>(); }
int& at( int const x, int const y )
{ return dummy<2>(); }
Mat2( int, int ) {}
};
struct Mat3
{
struct At { At( int x, int y ) {} };
int operator[]( At const i ) const
{ return dummy<3>(); }
int& operator[]( At const i )
{ return dummy<3>(); }
Mat3( int, int ) {}
};
class Mat4
{
protected:
int get( int const x, int const y ) const
{ return dummy<4>(); }
void set( int const x, int const y, int const v ) {}
class AssignmentProxy
{
private:
Mat4* pMat_;
int x_;
int y_;
public:
void operator=( int const v ) const
{ pMat_->set( x_, y_, v ); }
int value() const { return pMat_->get( x_, y_ ); }
operator int () const { return value(); }
AssignmentProxy( Mat4& mat, int const x, int const y )
: pMat_( &mat ), x_( x ), y_( y )
{}
};
public:
int operator()( int const x, int const y ) const
{ return get( x, y ); }
AssignmentProxy operator()( int const x, int const y )
{ return AssignmentProxy( *this, x, y ); }
Mat4( int, int ) {}
};
class Mat5
{
protected:
int at( int const x, int const y ) const
{ return dummy<4>(); }
int& at( int const x, int const y )
{ return dummy<5>(); }
class RowReadAccess
{
private:
Mat5 const* pMat_;
int y_;
public:
int operator[]( int const x ) const
{
return pMat_->at( x, y_ );
}
RowReadAccess( Mat5 const& m, int const y )
: pMat_( &m ), y_( y )
{}
};
class RowRWAccess
{
private:
Mat5* pMat_;
int y_;
public:
int operator[]( int const x ) const
{
return pMat_->at( x, y_ );
}
int& operator[]( int const x )
{
return pMat_->at( x, y_ );
}
RowRWAccess( Mat5& m, int const y )
: pMat_( &m ), y_( y )
{}
};
public:
RowReadAccess operator[]( int const y ) const
{ return RowReadAccess( *this, y ); }
RowRWAccess operator[]( int const y )
{ return RowRWAccess( *this, y ); }
Mat5( int, int ) {}
};
struct Mat6
{
private:
std::vector<int> elems_;
int width_;
int height_;
int indexFor( int const x, int const y ) const
{
return y*width_ + x;
}
public:
int const* operator[]( int const y ) const
{
return &elems_[indexFor( 0, y )];
}
int* operator[]( int const y )
{
return &elems_[indexFor( 0, y )];
}
Mat6( int const w, int const h )
: elems_( w*h, 6 ), width_( w ), height_( h )
{}
};
int main()
{
using namespace std;
enum{ w = 1024, h = 1024 };
typedef Mat3::At At;
Mat1 m1( w, h );
Mat2 m2( w, h );
Mat3 m3( w, h );
Mat4 m4( w, h );
Mat5 m5( w, h );
Mat6 m6( w, h );
wcout
<< m1( 100, 200 ) // No fuss simple, but exposes element ref.
<< m2.at( 100, 200 ) // For those who don't like operators.
<< m3[At( 100, 200)] // If you really want square brackets mnemonic.
<< m4( 100, 200 ) // Hides element ref by using assignment proxy.
<< m5[200][100] // Ditto but with square brackets (more complex).
<< m6[200][100] // The minimum fuss square brackets, exposes elem ref.
<< endl;
}
Oh well I discovered after posting that code that I haven't fully hidden the internal storage for Mat5: it needs an extra proxy level, as in Mat4. So that approach is really complex. I wouldn't do it (Mat1 is nice and easy I think), but some folks think proxys are cool, and data hiding even more cool…
Summing up, there is no “the” correct way to overload operator[]. There are many ways (as illustrated by the code above), each with some trade-offs. And generally you’re better off using operator(), because as opposed to operator[] it can take any number of arguments.
There is no [][] operator. The way GLM does it is by returning a vec3& from the first []. vec3 has its own [] operator overload. So it's two separate operator[]s on two separate classes.
This is also how GLSL works. The first [] gets the column as a vector. The second takes the vector and gets the value from it.
The expression foo[1][2] is really interpreted as (foo[1])[2], i.e. the [] operator is applied twice in succession starting with the variable foo. There is no [][] operator to be overloaded.

using of std::accumulate

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.