By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,143 Members | 1,908 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,143 IT Pros & Developers. It's quick & easy.

Design problem - proxy class acting as a reference to object

P: n/a
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::operator() (int coll, int row);

I could either replace it with

MyObject MyMatrix::operator() (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.
Jul 23 '05 #1
Share this Question
Share on Google+
8 Replies


P: n/a
On Sun, 24 Apr 2005 21:43:51 -0700, Kanenas wrote:
[...]
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...
Jul 23 '05 #2

P: n/a
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::operator()(size_t, size_t) const
{
return _object;
}

MyMatrix::MyMatrixElement MyMatrix::operator()(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::operator()(size_t col, size_t row)"
is called followed by "MyMatrixElement::operator const MyObject&()
const". In my opinion it would be far easier and faster to just call
"const MyObject& MyMatrix::operator()(size_t col, size_t row) const".

Can anyone explain this behaviour?

Thanks,
Mark
Jul 23 '05 #3

P: n/a
SpOiLeR wrote:
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::operator() (int coll, int row);

I could either replace it with

MyObject MyMatrix::operator() (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

Jul 23 '05 #4

P: n/a
On 25 Apr 2005 07:49:26 -0700, Axter wrote:
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.
Jul 23 '05 #5

P: n/a
On Mon, 25 Apr 2005 16:09:53 +0200, Capstar wrote:
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::operator()(size_t, size_t) const
{
return _object;
}

MyMatrix::MyMatrixElement MyMatrix::operator()(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::operator()(size_t col, size_t row)"
is called followed by "MyMatrixElement::operator const MyObject&()
const". In my opinion it would be far easier and faster to just call
"const MyObject& MyMatrix::operator()(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::operator()(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
Jul 23 '05 #6

P: n/a
Capstar wrote:
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]

Jul 23 '05 #7

P: n/a
SpOiLeR wrote:
On 25 Apr 2005 07:49:26 -0700, Axter wrote:
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.

Jul 23 '05 #8

P: n/a
On Mon, 25 Apr 2005 16:09:53 +0200, Capstar <ne**@ge.homeip.net>
wrote:
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::operator
MyObject()' that can't be handled with 'MyMatrixElement::operator
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::operator()(size_t col, size_t row)"
is called followed by "MyMatrixElement::operator const MyObject&()
const". In my opinion it would be far easier and faster to just call
"const MyObject& MyMatrix::operator()(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::operator()(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::operator()'. 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<...>::operator[](const Key& k)' creates an entry if none exists
for 'k'. There's no 'map<...>::operator[](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::out_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<...>::operator[](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::operator() const' is called, you can
use a cast to make 'matrix' const.

Kanenas
Jul 23 '05 #9

This discussion thread is closed

Replies have been disabled for this discussion.