Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Design problem - proxy class acting as a reference to object

Reply
Thread Tools

Design problem - proxy class acting as a reference to object

 
 
SpOiLeR
Guest
Posts: n/a
 
      04-24-2005
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:perator() (int coll, int row);

I could either replace it with

MyObject MyMatrix: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.
 
Reply With Quote
 
 
 
 
Kanenas
Guest
Posts: n/a
 
      04-25-2005
On Sun, 24 Apr 2005 12:31:42 +0200, SpOiLeR <request@no_spam.org>
wrote:

>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: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
 
Reply With Quote
 
 
 
 
SpOiLeR
Guest
Posts: n/a
 
      04-25-2005
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...
 
Reply With Quote
 
Capstar
Guest
Posts: n/a
 
      04-25-2005
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:perator()(size_t, size_t) const
{
return _object;
}

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

Can anyone explain this behaviour?

Thanks,
Mark
 
Reply With Quote
 
Axter
Guest
Posts: n/a
 
      04-25-2005
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:perator() (int coll, int row);
>
> I could either replace it with
>
> MyObject MyMatrix: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

 
Reply With Quote
 
SpOiLeR
Guest
Posts: n/a
 
      04-27-2005
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.
 
Reply With Quote
 
SpOiLeR
Guest
Posts: n/a
 
      04-27-2005
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:perator()(size_t, size_t) const
> {
> return _object;
> }
>
> MyMatrix::MyMatrixElement MyMatrix: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:perator()(size_t col, size_t row)"
> is called followed by "MyMatrixElement:perator const MyObject&()
> const". In my opinion it would be far easier and faster to just call
> "const MyObject& MyMatrix: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: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
 
Reply With Quote
 
red floyd
Guest
Posts: n/a
 
      04-27-2005
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]

 
Reply With Quote
 
Axter
Guest
Posts: n/a
 
      04-28-2005
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.

 
Reply With Quote
 
Kanenas
Guest
Posts: n/a
 
      05-30-2005
On Mon, 25 Apr 2005 16:09:53 +0200, Capstar <(E-Mail Removed)>
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:perator
MyObject()' that can't be handled with 'MyMatrixElement: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:perator()(size_t col, size_t row)"
>is called followed by "MyMatrixElement:perator const MyObject&()
>const". In my opinion it would be far easier and faster to just call
>"const MyObject& MyMatrix: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: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: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<...>:perator[](const Key& k)' creates an entry if none exists
for 'k'. There's no 'map<...>: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: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<...>: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:perator() const' is called, you can
use a cast to make 'matrix' const.

Kanenas
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Instance variable acting like class variable? Mike Mr. Ruby 4 09-21-2008 10:15 PM
"Object reference not set to an instance of an object" Weird thing happens with reference a link nguyentrongkha@gmail.com ASP .Net 1 09-20-2007 09:46 PM
Weakref problem: no way to get original object from proxy object? John Nagle Python 2 02-26-2007 06:34 AM
Nested Class, Member Class, Inner Class, Local Class, Anonymous Class E11 Java 1 10-12-2005 03:34 PM
acting as a proxy server Angela Perl Misc 2 11-25-2004 04:37 PM



Advertisments