Design problem - proxy class acting as a reference to object

S

SpOiLeR

Hello!

I have a matrix class like this:

class MyObject;

// MyMatrix is contains MyObjects
class MyMatrix {
public:
...
MyObject& operator() (int coll, int row);
...
private:
}

Now, this allows me writing code like this:

MyMatrix mat;
MyObject obj1, obj2;

mat (2,3) = obj1;
obj2 = mat (3,2);

So far, so good.
MyMatrix also keeps some private constants that hold coordinates of some
special MyObjects, so retrieving of those special MyObjects is faster. For
example:

struct coordinates {
int col, row;
}

class MyMatrix {
public:
...
MyObject& operator() (int coll, int row);
...
private:
coordinates specialMyObject;
}

Because of operator(), client of MyMatrix is allowed to freely edit
contents of MyMatrix object, thus invalidating specialMyObject.
Now, I could write publicly available method Refresh () that refreshes
state of internal constants. There are two ways how this methods could be
used:

1.) Client must call it after editing MyMatrix object.
2.) It is called internally, by each method that relies on internal
constants, before those methods start their work.

Number 2.) is definitely bad because keeping state of object so it doesn't
have to be checked thoroughly each time needed, and then check it
thoroughly each time it is needed, completely nullifies purpose of internal
constants.

Number 1.) works fine, but from the OO view looks bad, because object
should take care of its internal data, not the client of that object. So,
there is something wrong with:

MyObject& MyMatrix::eek:perator() (int coll, int row);

I could either replace it with

MyObject MyMatrix::eek:perator() (int coll, int row);

and add method:

void MyMatrix::AsignObject (int coll, int row, const MyObject& obj);

but this invalidates expression:

mat (2,3) = obj1;

Or I could write some kind of proxy class that acts as a reference to
MyObject but also changes state of MyMatrix object when needed.

What would that proxy class look like? I don't know where to start from, so
links or google search keywords would be greatly appreciated.
 
K

Kanenas

Hello!

I have a matrix class like this:
[...]
MyMatrix also keeps some private constants that hold coordinates of some
special MyObjects, so retrieving of those special MyObjects is faster. For
example:

struct coordinates {
int col, row;
}

class MyMatrix {
public:
...
MyObject& operator() (int coll, int row);
...
private:
coordinates specialMyObject;
}
[...]
Or I could write some kind of proxy class that acts as a reference to
MyObject but also changes state of MyMatrix object when needed.

What would that proxy class look like? I don't know where to start from, so
links or google search keywords would be greatly appreciated.

Let's call the helper "MyMatrixElement". It could contain a reference
to the MyMatrix object it's supposed to watch over and to an element
of MyMatrix. Each MyMatrixElement::eek:perator= can call the method of
MyMatrix which updates MyMatrix's state.

class MyMatrix {
private:
class MyMatrixElement {
private:
MyMatrix& watched;
MyObject& el;
size_t col, row;

public:
MyMatrixElement(MyMatrix& matr, MyObject& obj,
size_t c, size_t r)
: watched(matr), el(obj), col(c), row(r) {}
template <typename _Type>
MyMatrixElement& operator=(_Type from)
{ el = from; watched.refresh(col, row); return *this;}
operator MyObject() { return el; }
operator const MyObject&() const { return el; }
}

public:
MyMatrixElement operator() (size_t col, size_t row);
const MyObject& operator() const (size_t col, size_t row);
}

Making class MyMatrixElement private in MyMatrix means
MyMatrixElements can only be stored in temporaries outside of
MyMatrix, so you don't have to worry about old MyMatrixElements
floating about. If you want to allow a client to store a
MyMatrixElement in a local (or what have you), make MyMatrixElement
public, in which case you want to be careful no MyMatrixElement
outlasts the MyMatrix and MyObject it refers to. Play around with the
above and see if it works for you.

Note that MyMatrixElement requires a non-const MyMatrix. This works
fine, as a const MyMatrix doesn't need a proxy for operator(). The
'operator() const' ensures a MyMatrixElement is never created for a
const MyMatrix.

Kanenas
 
S

SpOiLeR

[...]
Play around with the above and see if it works for you.

[...]

Kanenas

After some adjustments for my real code, your code works perfectly. Thank
you very much, it is exactly what I needed...
 
C

Capstar

Just out of curiosity, I tried the following:

/** matrix.h **/
#ifndef MATRIX_H__
#define MATRIX_H__

class MyMatrix
{
private:
class MyMatrixElement
{
private:
MyMatrix& watched;
MyObject& el;
size_t col, row;

public:
MyMatrixElement(MyMatrix& matr, MyObject& obj,
size_t c, size_t r)
: watched(matr), el(obj), col(c), row(r) {}

template <typename _Type>
MyMatrixElement& operator=(_Type from)
{ el = from; watched.refresh(col, row); return *this;}

//operator MyObject() { return el; }
operator const MyObject&() const { return el; }
};

MyObject _object;

void refresh(size_t, size_t) {}

public:
const MyObject& operator()(size_t col, size_t row) const;
MyMatrixElement operator()(size_t col, size_t row);
};

#endif /*MATRIX_H__*/



/** matrix.cpp **/
class MyObject
{
public:
MyObject & add(int) { return *this; }
};

//typedef int MyObject;

#if defined __GNUC__
typedef unsigned long size_t;
#endif

#include "matrix.h"

const MyObject& MyMatrix::eek:perator()(size_t, size_t) const
{
return _object;
}

MyMatrix::MyMatrixElement MyMatrix::eek:perator()(size_t col, size_t row)
{
return MyMatrixElement(*this, _object, col, row);
}

int main(void)
{
MyMatrix matrix;
MyObject obj;

obj = matrix(0, 0); /* weird behaviour */
matrix(1, 1) = obj;
matrix(3, 3) = MyObject(matrix(2, 2)).add(1);

return 0;
}


As you can see I removed "operator MyObject() { return el; }" since
Visual studio wouldn't compile with both that one and "operator const
MyObject&() const { return el; }".
And I don't realy see the use in both of them anyway, but that could be
caused by my inexpierience.

Now the main thing I'm wondering is at the line I commented with weird
behaviour, I noticed that with Visual Studio as well as gcc the method
"MyMatrix::MyMatrixElement MyMatrix::eek:perator()(size_t col, size_t row)"
is called followed by "MyMatrixElement::eek:perator const MyObject&()
const". In my opinion it would be far easier and faster to just call
"const MyObject& MyMatrix::eek:perator()(size_t col, size_t row) const".

Can anyone explain this behaviour?

Thanks,
Mark
 
A

Axter

SpOiLeR said:
Hello!

I have a matrix class like this:

class MyObject;

// MyMatrix is contains MyObjects
class MyMatrix {
public:
...
MyObject& operator() (int coll, int row);
...
private:
}

Now, this allows me writing code like this:

MyMatrix mat;
MyObject obj1, obj2;

mat (2,3) = obj1;
obj2 = mat (3,2);

So far, so good.
MyMatrix also keeps some private constants that hold coordinates of some
special MyObjects, so retrieving of those special MyObjects is faster. For
example:

struct coordinates {
int col, row;
}

class MyMatrix {
public:
...
MyObject& operator() (int coll, int row);
...
private:
coordinates specialMyObject;
}

Because of operator(), client of MyMatrix is allowed to freely edit
contents of MyMatrix object, thus invalidating specialMyObject.
Now, I could write publicly available method Refresh () that refreshes
state of internal constants. There are two ways how this methods could be
used:

1.) Client must call it after editing MyMatrix object.
2.) It is called internally, by each method that relies on internal
constants, before those methods start their work.

Number 2.) is definitely bad because keeping state of object so it doesn't
have to be checked thoroughly each time needed, and then check it
thoroughly each time it is needed, completely nullifies purpose of internal
constants.

Number 1.) works fine, but from the OO view looks bad, because object
should take care of its internal data, not the client of that object. So,
there is something wrong with:

MyObject& MyMatrix::eek:perator() (int coll, int row);

I could either replace it with

MyObject MyMatrix::eek:perator() (int coll, int row);

and add method:

void MyMatrix::AsignObject (int coll, int row, const MyObject& obj);

but this invalidates expression:

mat (2,3) = obj1;

Or I could write some kind of proxy class that acts as a reference to
MyObject but also changes state of MyMatrix object when needed.

What would that proxy class look like? I don't know where to start from, so
links or google search keywords would be greatly appreciated.

I still don't completely understand why you would need the proxy class,
and what the proxy class would do for you in this requirement, however
consider the following method for a matrix class, that allows the use
of syntax similar to C-Style [][] double index brackets.
See following link:
http://code.axter.com/dynamic_2d_array.h
You can use it like this:
int SizeX = 3;
int SizeY = 5;
dynamic_2d_array<MyObject> mat(SizeX, SizeY);
MyObject obj1, obj2;
mat[2][3] = obj1;
obj2 = mat[2][3];

This type of syntax is more natural for C/C++ environment, and
therefore IMHO, easier to read.

See following link for a few more different versions of above
dynamic_2d_array class, including one version that uses std::vector,
and allows for resize.
http://www.tek-tips.com/faqs.cfm?fid=5575
 
S

SpOiLeR

SpOiLeR wrote:
[...]

I still don't completely understand why you would need the proxy class,

Then re-read original post... I want to update MyObject's internals without
client needing to interfere in that.
of syntax similar to C-Style [][] double index brackets.

FAQ has something about this, and why it should better to be avoided, and
replaced with operator()...
See following link:
http://code.axter.com/dynamic_2d_array.h
You can use it like this:
int SizeX = 3;
int SizeY = 5;
dynamic_2d_array<MyObject> mat(SizeX, SizeY);
MyObject obj1, obj2;
mat[2][3] = obj1;
obj2 = mat[2][3];

This type of syntax is more natural for C/C++ environment, and
therefore IMHO, easier to read.

Thanks, but that's not what I wanted, anyway check Kanennas post above to
see what I mean.
 
S

SpOiLeR

Just out of curiosity, I tried the following:

/** matrix.h **/
#ifndef MATRIX_H__
#define MATRIX_H__

class MyMatrix
{
private:
class MyMatrixElement
{
private:
MyMatrix& watched;
MyObject& el;
size_t col, row;

public:
MyMatrixElement(MyMatrix& matr, MyObject& obj,
size_t c, size_t r)
: watched(matr), el(obj), col(c), row(r) {}

template <typename _Type>
MyMatrixElement& operator=(_Type from)
{ el = from; watched.refresh(col, row); return *this;}

//operator MyObject() { return el; }
operator const MyObject&() const { return el; }
};

MyObject _object;

void refresh(size_t, size_t) {}

public:
const MyObject& operator()(size_t col, size_t row) const;
MyMatrixElement operator()(size_t col, size_t row);
};

#endif /*MATRIX_H__*/



/** matrix.cpp **/
class MyObject
{
public:
MyObject & add(int) { return *this; }
};

//typedef int MyObject;

#if defined __GNUC__
typedef unsigned long size_t;
#endif

#include "matrix.h"

const MyObject& MyMatrix::eek:perator()(size_t, size_t) const
{
return _object;
}

MyMatrix::MyMatrixElement MyMatrix::eek:perator()(size_t col, size_t row)
{
return MyMatrixElement(*this, _object, col, row);
}

int main(void)
{
MyMatrix matrix;
MyObject obj;

obj = matrix(0, 0); /* weird behaviour */
matrix(1, 1) = obj;
matrix(3, 3) = MyObject(matrix(2, 2)).add(1);

return 0;
}


As you can see I removed "operator MyObject() { return el; }" since
Visual studio wouldn't compile with both that one and "operator const
MyObject&() const { return el; }".

gcc works fine, don't have MSVC, so can't try it. But IMHO it should work!
And I don't realy see the use in both of them anyway, but that could be
caused by my inexpierience.
Now the main thing I'm wondering is at the line I commented with weird
behaviour, I noticed that with Visual Studio as well as gcc the method
"MyMatrix::MyMatrixElement MyMatrix::eek:perator()(size_t col, size_t row)"
is called followed by "MyMatrixElement::eek:perator const MyObject&()
const". In my opinion it would be far easier and faster to just call
"const MyObject& MyMatrix::eek:perator()(size_t col, size_t row) const".

Can anyone explain this behaviour?

Thanks,
Mark

If you have const MyMatrix object, you won't be able to call constructor
for MyMatrixElement because it takes non-const reference to MyMatrix. So,
for const MyMatrix you will need to have

const MyObject& MyMatrix::eek:perator()(size_t col, size_t row) const

as a reference operator, because const object would'n be able to call

MyMatrixElement operator()(size_t col, size_t row);

So, you need both.



If you have non const MyMatrix object, and ask for non const Myobject&:

MyMatrix mat;
Myobject& objref = mat (2,3);

you will get compile time error because there MyMatrixElement can't be
converted into non const Myobject&. This is exactly what was wanted,
because we don't want client to reference directly MyObject
unless it just reads them (either by obtaining copy, or const&).

That's why you need both:

operator MyObject() { return el; }
operator const MyObject&() const { return el; }

So, to conclude, you can either get copy or const reference, but if you
want to change anything on MyObject you will have to do it through
reference operator, i.e.

matrix (2,3) = obj; // This would refresh our matrix
 
R

red floyd

Capstar said:
Just out of curiosity, I tried the following:

/** matrix.h **/
#ifndef MATRIX_H__
#define MATRIX_H__

Don't do this. Any identifier with two consecutive underscores is
reserved to the implementation. Use MATRIX_H_ instead.
[redacted]
 
A

Axter

SpOiLeR said:
SpOiLeR wrote:
[...]

I still don't completely understand why you would need the proxy
class,

Then re-read original post... I want to update MyObject's internals without
client needing to interfere in that.
of syntax similar to C-Style [][] double index brackets.

FAQ has something about this, and why it should better to be avoided, and
replaced with operator()...

I've read the FAQ, and the information on the FAQ is base on a method
that returns a temporary Type to allow access to second [] index.
My class does not use that method, and therefore the class I posted is
much more efficient.
It's as efficient, if not more efficient then using operator().
Moreover, it's more compatible to C-Style syntax, and therefore easier
to use and read.
It's much better to use common syntax methods, then to use methods that
result in having to use ambiguous syntax.
 
K

Kanenas

Just out of curiosity, I tried the following:
[...]

int main(void)
{
MyMatrix matrix;
MyObject obj;

obj = matrix(0, 0); /* weird behaviour */
matrix(1, 1) = obj;
matrix(3, 3) = MyObject(matrix(2, 2)).add(1);

return 0;
}


As you can see I removed "operator MyObject() { return el; }" since
Visual studio wouldn't compile with both that one and "operator const
MyObject&() const { return el; }".
And I don't realy see the use in both of them anyway, but that could be
caused by my inexpierience.
I put that in mostly out of habit. Normally when there's a conversion
operator returning a reference, there will be a corresponding
conversion operator differing only in its const-ness and the
const-ness of the returned reference.

Offhand, I can't think of a situation using 'MyMatrixElement::eek:perator
MyObject()' that can't be handled with 'MyMatrixElement::eek:perator
const MyObject&()' and possibly a temporary created via MyObject's
copy constructor.
Now the main thing I'm wondering is at the line I commented with weird
behaviour, I noticed that with Visual Studio as well as gcc the method
"MyMatrix::MyMatrixElement MyMatrix::eek:perator()(size_t col, size_t row)"
is called followed by "MyMatrixElement::eek:perator const MyObject&()
const". In my opinion it would be far easier and faster to just call
"const MyObject& MyMatrix::eek:perator()(size_t col, size_t row) const".

Can anyone explain this behaviour?
Here are three reasons for you to consider:

1) matrix is non-const, so matrix(0,0) binds to the non-const
'MyMatrix::eek:perator()(size_t, size_t)'. Const-overloading resolution
is modeled thusly: non-static methods are given an "implicit object
parameter". E.g. for 'X::f(...)' the implicit object parameter is
'X&' and for 'X::f(...) const' it's 'const X&'. Consider:
X x;
x.f(/* ... */);
From the view of the resolver, binding 'x.f' to 'X::f(...) const'
requires an implicit conversion of 'x' to a 'const X&' while binding
'x.f' to 'X::f(...)' requires no conversions. 'X::f(...)' is thus the
best viable function.

2) Picking the const version over the non-const as being the "better"
would require examining the context of the call to
'MyMatrix::eek:perator()'. This is outside the realm of C++ semantic
context sensitivity.

3) (a biggie) An optimizer could examine context and pick the const
version, but this may produce undesirably different behavior (which
includes return type, return value, exceptions and side effects) than
calling the non-const operator(). There's no semantic or (except for
some operators) connotative connection between const-overloaded
methods at the language level; though they may often be semantically
similar when implemented, that's only a matter of convention. Even
with operator[], the const version can be semantically different from
the non-const version.
As an example, consider map from the STL.
'map<...>::eek:perator[](const Key& k)' creates an entry if none exists
for 'k'. There's no 'map<...>::eek:perator[](const Key& k) const', but
it's easy to imagine an extended library which defined one. The const
[] shouldn't create an entry; instead, it could throw an out_of_range
exception. Suppose compilers were allowed to call the const indexing
operator when its result is used as an rvalue and consider the
following fragment:
map<std::string, int> settings;
//...
try {
//if "debug_level" isn't set, it defaults to 0
dbg_lvl = settings["debug_level"];
} catch (std::eek:ut_of_range) {dbg_lvl=0;}
For safety's sake, you have to catch a possible out_of_range
exception. If the compiler ends up binding 'foo[name]' to
'map<...>::eek:perator[](std::string)', no out_of_range is ever thrown
but you still incur the overhead of the try/catch block. Note also
that all the try/catch does is produce the behavior of the non-const
[]. IMO, blech.

If you want to ensure 'MyMatrix::eek:perator() const' is called, you can
use a cast to make 'matrix' const.

Kanenas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
474,262
Messages
2,571,058
Members
48,769
Latest member
Clifft

Latest Threads

Top