Implementing operator[][] using proxy class. Works fine for single 2-dimensional data structure which can be accessed by some double data(int row, in col) method. However if I want to have few data methods I'd like to use pointer to member function which is stored in proxy inner-class TPatternRow:
class TLearnPattern
{
public:
typedef int (TLearnPattern::* IndexFunction)(int);
typedef double (TLearnPattern::* DataFunction)(int, int);
TLearnPattern(TDataSource *source);
class TPatternRow ///< Proxy class to implement operator[][]
{
public:
TPatternRow(TLearnPattern& source, int row, DataFunction func) :
lp(source),
i(row),
data(func)
{}
double& operator[](int j) { return (lp.*data)(i, j); }
private:
TLearnPattern& lp;
DataFunction data = &TLearnPattern::in;
int i;
};
TPatternRow operator [](int i) { return TPatternRow(*this, i, &TLearnPattern::in); }
TPatternRow In(int i) { return TPatternRow(*this, i, &TLearnPattern::in); }
TPatternRow Out(int i) { return TPatternRow(*this, i, &TLearnPattern::out); }
virtual double in(int row, int col) final;
virtual double out(int row, int col) final;
}
How to syntactically correctly call following line?
double& operator[](int j) { return (lp.*data)(i, j); }
Use std::bind? How to correctly bind this in TPatternRow constructor?
And some related question: does bind works fast enough comparing to just pointer to member function call?
Solved like this:
double operator[](int j)
{
auto dataFunc = std::bind(data, lp, _1, _2);
return dataFunc(i, j);
}
or original without &:
double operator[](int j) { return (lp.*data)(i, j); }
Related
I have a class which allows me to access a vectors value:
Class Image{
public:
Image(int rows, int cols): cols_(cols), rows_(rows), data_(rows*cols,0) {};
int& at(int row, int col){
return data_.at(col*row);
};
private:
int rows_ = 0;
int cols_ = 0;
const int max_val_ = 255;
std::vector<int> data_;
Currently this lets me perform
int num = image.at(row, col);
// or
image.at(row, col) = 10;
My question is how to I limit the values of data_ to not allow an assignment more than max_value_? I.e image.at(row,col) = 256;
Like Max Langhof said, you can use a proxy object like this:
class AssignmentProxy
{
public:
AssignmentProxy(int& value, int min, int max) : value_(value), min_(min), max_(max) { }
AssignmentProxy& operator=(int value)
{
if (value >= min_ && value < max_)
value_ = value;
else
throw std::invalid_argument("no");
return *this;
}
operator int() const noexcept
{
return value_;
}
private:
int min_, max_;
int& value_;
};
The implicit conversion to int is questionable but justified I think, since this only a proxy that mimics an integer.
Here is a full example.
I'm having a problem to make the code:
void main(){
Matrix c(rows,cols);//rows & cols are int numbers
c[0][0]=2//the line that I'm having a problem to do the operator
}
//My class defined like this:
class Matrix{
public:
Matrix(int rows,int cols): rows(rows), cols(cols){
mat= new double*[cols];
for( int i=0; i<rows;i++){
*mat=new double[i];
}
}
private:
int rows,cols;
double **mat;
};
How can I make an operator that will help me to do the line that I'm having a problem with?
There are no operator [][], but operator[]. So that one should return something for which you can use [] too (pointer or proxy class).
In your case, you might simply do:
double* operator[](int i) { return mat[i]; }
const double* operator[](int i) const { return mat[i]; }
For more complicated cases, you have to return a proxy class.
Don't dynamically allocate in two dimensions like that. It's poison for your cache, and completely pointless. I see it all the time and I wish I didn't! Make yourself a nice std::vector<double> of size rows*cols instead.
Anyway, the trick to permit [width][height] is a proxy class. Have your operator[] return an instance of a class that has its own operator[] to do the second-level lookup.
Something like this:
#include <iostream>
#include <vector>
struct Matrix
{
Matrix(const size_t columns, const size_t rows)
: columns(columns)
, rows(rows)
, data(columns*rows, 0)
{}
size_t index(const size_t x, const size_t y) const
{
return x + y*columns;
}
double& at(const size_t x, const size_t y)
{
return data[index(x, y)];
}
double at(const size_t x, const size_t y) const
{
return data[index(x, y)];
}
template <bool Const>
struct LookupHelper
{
using ParentType = std::conditional_t<Const, const Matrix, Matrix>;
using ReturnType = std::conditional_t<Const, double, double&>;
LookupHelper(ParentType& parent, const size_t x) : parent(parent), x(x) {}
ReturnType operator[](const size_t y)
{
return parent.data[parent.index(x, y)];
}
const ReturnType operator[](const size_t y) const
{
return parent.data[parent.index(x, y)];
}
private:
ParentType& parent;
const size_t x;
};
LookupHelper<false> operator[](const size_t x)
{
return {*this, x};
}
LookupHelper<true> operator[](const size_t x) const
{
return {*this, x};
}
private:
const size_t columns, rows;
std::vector<double> data;
};
int main()
{
Matrix m(42, 3);
m[15][3] = 1;
std::cout << m[15][3] << '\n';
}
(In reality, you'd want to make it moveable and it could doubtlessly be tidied up a bit.)
Certainly, switching to operator() or a .at(width, height) member function is a lot easier…
Considering the following toy example, where I declare a class which encapsulates ublas from boost libraries:
#include <boost/numeric/ublas/matrix_sparse.hpp>
#include <iostream>
namespace ublas = boost::numeric::ublas;
class UblasEncapsulated {
public:
ublas::compressed_matrix<float>::reference operator()(int i, int j){
std::cout << "Non const reference" << std::endl;
MtrUpdated_ = true;
return mtr_(i, j);
}
ublas::compressed_matrix<float>::const_reference operator()(
int i, int j) const {
std::cout << "Const reference" << std::endl;
return mtr_(i, j);
}
UblasEncapsulated() { MtrUpdated = false; }
private:
ublas::compressed_matrix<float> mtr_(3, 3);
bool MtrUpdated_;
};
int main() {
UblasEncapsulated foo;
foo(2, 0) = 1.0f;
float const foo_float = foo(2, 0);
return 0;
}
I was expecting the output
Non constant reference
Constant reference
But I got
Non constant reference
Non constant reference
What am I doing wrong? How can I properly track when mtr_ could have its values changed?
foo is non-const, so the non-const version of foo.operator() will be called. It doesn't matter how the value it returns is used.
If you really want to know that MtrUpdated_ is only set true if an element is actually assigned to, you will need to use a proxy class:
class UblasEncapsulated {
public:
class proxy {
public:
proxy(UblasEncapsulated* ptr, int i, int j)
: ptr_(ptr), i_(i), j_(j)
{}
proxy& operator=(float f) {
ptr_->MtrUpdated_ = true;
ptr_->mtr_(i_, j_) = f;
return *this;
}
operator float() {
return ptr_->mtr_(i_, j_);
}
private:
UblasEncapsulated* ptr_;
int i_;
int j_;
};
proxy operator()(int i, int j) {
return proxy(this, i, j);
}
ublas::compressed_matrix<float>::const_reference operator() (int i, int j) const {
return mtr_(i, j);
}
UblasEncapsulated()
: mtr_(3, 3),
MtrUpdated_(false)
{}
private:
ublas::compressed_matrix<float> mtr_;
bool MtrUpdated_;
};
Live Demo
Note that you should avoid using a proxy class if you can get away with it since it doesn't play nicely with things like auto or template argument deduction.
Example code:
class Sum{
public:
void value(int val){_v = val;}
int add(int val){return _v + val;}
private:
int _v;
};
Sum sum;
sum.value(5);
int myResult = sum.add(10);
Is it possible to make the sum.add(10) default so I can get the same result like this:
int myArea = sum(10);
(I suspect the question title is wrong, perhaps it is the reason why I can't find a solution. Feel free to edit.)
Yes you can. What you can do is overload the operator() of the class to turn it into a functor. You can add
int operator()(int val) { return add(val); }
To the class and now sum(some_value) is the same thing as sum.add(some_value)
That's specifically what the function call operator is for.
class Sum{
public:
void value(int val){ _v = val; }
int add(int val) const{ return _v + val; }
// Function call operator
int operator()(int val) const {
return add(val);
}
private:
int _v;
};
int main()
{
Sum sum;
sum.value(5);
// Calls the function call operator
int myResult = sum(10);
}
I am using GNU Scientific Library in my C++ project. For convience, I would like to trasparently wrap gsl_vector* in a C++ class (to add a bunch of domain specific functions and to simplify interface). But I am getting perplexed with how to deal with const gsl_vector*. Let me explain. Let me start with this minimalistic wrapper.
class Vector {
gsl_vector* vector_;
public:
Vector(gsl_vector* vector): vector_(vector) {}
double& operator()(int i) {
return *gsl_vector_ptr(vector_, i);
}
};
Suppose, further, that I have two functions. One is defined as follows:
int f(Vector& x) {
\\ do some math, e.g. x(0) = 0.0;
return 0;
}
Another one is a callback function that has to use GSL types, and is defined as follows:
int gsl_f(gsl_vector* x) {
Vector xx(x);
return f(xx);
}
This works fine. Now, suppose the callback has a constant signature:
int gsl_f(const gsl_vector* x);
Then I can redefine my Vector class and my f function accordingly:
class Vector {
const gsl_vector* vector_;
public:
Vector(const gsl_vector* vector): vector_(vector) {}
const double& operator()(int i) const {
return *gsl_vector_const_ptr(vector_, i);
}
};
int f(const Vector& x) {
\\ do some math
return 0;
}
Also works. Now, I want my wrapper class to suit both situations. For example, I want to be able to do the following, preserving the safety of const:
int f(const Vector& x, Vector& y) {
\\ do some math
return 0;
}
int gsl_f(const gsl_vector* x, gsl_vector* y) {
Vector yy(y);
return f(x, yy);
}
I can do it by having a Vector with two pointers, const and non-const, and remembering whether it was initialized from a const or non-const member. My question is, can I do it without runtime checks? After all, all the information is there at the compile time.
Proposal (not wonderful, but should work):
class Vector {
gsl_vector* vector_;
const gsl_vector* const_vector_;
public:
Vector(const gsl_vector* vector): vector_(nullptr), const_vector_(vector) {}
Vector(gsl_vector* vector): vector_(vector), const_vector_(vector) {}
const double& operator()(int i) const {
return *gsl_vector_ptr(const_vector_, i);
}
double& operator () (int i) {
return *gsl_vector_ptr(vector_, i);
}
};
Second possibility:
class Vector {
private:
gsl_vector* vector_;
Vector(gsl_vector* vector): vector_(vector) {}
public:
static const Vector* Create (const gsl_vector* vector) {
return new Vector (const_cast<Vector *> vector);
}
static Vector* Create (gsl_vector* vector) {
return new Vector (vector);
}
const double& operator()(int i) const {
return *gsl_vector_ptr(vector_, i);
}
double& operator () (int i) {
return *gsl_vector_ptr(vector_, i);
}
};
Combining both classes should work as expected, have you tried it?
class Vector {
gsl_vector* vector_;
public:
Vector(gsl_vector* vector): vector_(vector) {}
const double& operator()(int i) const {
return *gsl_vector_ptr(vector_, i);
}
double& operator()(int i) {
return *gsl_vector_ptr(vector_, i);
}
operator const_Vector()
{
return const_Vector(vector_);
}
};
class const_Vector {
const gsl_vector* vector_;
public:
const_Vector(const gsl_vector* vector): vector_(vector) {}
const double& operator()(int i) const {
return *gsl_vector_ptr(vector_, i);
}
};
Function signature needs to look this way:
int f(const_Vector& x, Vector& y) {
\\ do some math
return 0;
}
This followes a similar scheme like the iterator and const_iterator.
Maybe you have a situation which this will not work,. you should post this situation and we can try to solve it.
You can also use some kind of inheritance with pointer to data. further - templates can be used to create overloaded function returning either one or second version depending on input pointer types.
class JSONChannelFullConfigConst:
public JSONObjectRenderer {
public:
JSONChannelFullConfigConst(const uint8_t * channel_id,
const sensors_single_channel_config_t * cfg) :
j_channel_id(channel_id),
j_cfg(cfg) {
}
private:
const uint8_t * const j_channel_id;
const sensors_single_channel_config_t * const j_cfg;
void renderFields(rlf::UcOstreamBase& os) const;
public:
uint8_t getId() const {
return *j_channel_id;
}
};
class JSONChannelFullConfig:
public JSONChannelFullConfigConst,
public JSONObjectParser {
public:
JSONChannelFullConfig(uint8_t * channel_id, sensors_single_channel_config_t * cfg) :
JSONChannelFullConfigConst(channel_id, cfg),
j_channel_id(channel_id),
j_cfg(cfg) {
}
void setId(uint8_t id) {
*j_channel_id = id;
}
private:
uint8_t * const j_channel_id;
sensors_single_channel_config_t * const j_cfg;
virtual bool parseNameValuePair(const char * name, rlf::UcIstream & value);
};