Can we override [][] ?

T

Thomas J. Gritzan

Tomás said:
Jim Langston posted:





Platform-specific code, I realise. (What you're trying to do can be quite
easily achieved portably though.)


However, even if you're guaranteed that:

(1) "unsigned long" is four bytes.
(2) Your target architecture is Bigendian.

The compiler is still within its rights to put padding between any of B G
R A.

The OP seems to operate on a little endian machine.
// Lets just prove that Intel is Bigendian.

I hope the code does not depend on it. :)

Thomas
 
E

Earl Purple

Jack said:
struct MagicInt {
// operator overloads, constructors, etc, to make this class behave
// as an integer.
};

std::pair<MagicInt, MagicInt> operator , (const MagicInt &a, const
MagicInt& b) { return std::make_pair(a, b); }

class Array2d {
public:
value& operator[](const std::pair<MagicInt, MagicInt> &a) {
// use a.first and a.second to find the value...
}
};

int main() {
Array2d M(X, Y);

for (MagicInt a = 0; a < X; ++a)
for (MagicInt b = 0; b < Y; ++b)
M[a, b] = i + j;
}


So, 'M[a, b]' is *implementable*, even if so brittlely that you'd never
want to use it. (For instance, you cannot say 'M[1, 2]'.)

Jack Saalweachter

Submit that to boost - all you have to do is implement MagicInt and it
will probably become part of their "not so important" libraries. (A bit
like lambda)
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Tomás said:
Martin Jørgensen posted:


board[3][5] = ChessBoard::Square::bishop;


I didn't understood how [3][5] got converted to Square *, since it only
takes an unsigned const as argument?



Analyse the following statement:


board[3][5] = ChessBoard::Square::bishop;


If you look up an operator precedence table, you'll see that it's
identical to:


( board[3] )[5] = ChessBoard::Square::bishop;


It's plain to see that operator[] is applied to "board". If we look at
the definition of this member function, we see that it returns a Square*,
i.e. a pointer. So it becomes:

(p)[5] = ChessBoard::Square::bishop;


When you apply [] to an intrinsic type, it evaluates to:


*(p + 5) = ChessBoard::Square::bishop;


So, looking at it again from the start:

board[3] evaluates to a pointer to a Square.
The block brackets are then applied to the pointer in order to access
an element at a particular index relative to the original pointer value.

Thanks - I also thought it must be something like this....

Just another little thing:

Square &operator=( SquareContents const sc ) didn't have to return *this
for the program to work, did it?

I guess the reason for having "return *this" is that you can then do
something like (not that it might be a good idea here, but isn't it
possible):

ChessBoard b1, b2, b3;

b1[3][5] = b2[0][4] = b3[3][3] = ChessBoard::Square::bishop;

?


Best regards
Martin Jørgensen
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Jack said:
Martin said:
You didn't really answer my questions. I think location 20 would be
[3][4] since that looks like *(squares + 8 * 2)[4], but I just wanted
to be sure.

You are correct: I completely misunderstood what you were asking.

Deep down, it shouldn't matter how you access location 20; what gets
mapped to location 20 is effectively 'implementation defined'. The goal
of the chessboard class is to abstract away the array and just give you
array-like access; if you can say 'board[3][4]' to refer to a location
on the board, why would you ever want to try to refer to location 20 in
the underlying array?

Nobody wants that. It's just a matter of understanding the code.
Just to be mean, the chessboard class could implement its op[] like so:

Square *operator[](unsigned const x)
{
return squares + 8 * (8 - x - 1);
}

From the outside, the chessboard behaves the same; however, location 20
is no longer mapped to board[3][4].

That doesn't give any sense.

You changed:

"return squares + 8 * (x-1)" -> "return squares + 8 * (7 - x)"

Original case: x = 2 => 8, your case: x = 2 => 40.

What are you trying to do?


Best regards
Martin Jørgensen
 
T

Tomás

Martin Jørgensen posted:

Square &operator=( SquareContents const sc ) didn't have to return
*this for the program to work, did it?


Not it didn't need to return a reference to itself, we could have had:

void &operator=( ...

But that wouldn't be very C++-like, because an assignment results in an
L-value in C++, e.g.:

a = b = c;


Just for some trivia, an assignment results in an R-value in C, so the
following is illegal in C:


a = b = c;

I guess the reason for having "return *this" is that you can then do
something like (not that it might be a good idea here, but isn't it
possible):

ChessBoard b1, b2, b3;

b1[3][5] = b2[0][4] = b3[3][3] = ChessBoard::Square::bishop;


Exactly.


-Tomás
 
T

Thomas J. Gritzan

Tomás said:
Martin Jørgensen posted:






Not it didn't need to return a reference to itself, we could have had:

void &operator=( ...

void operator=( ...
But that wouldn't be very C++-like, because an assignment results in an
L-value in C++, e.g.:

a = b = c;


Just for some trivia, an assignment results in an R-value in C,
correct.

... so the following is illegal in C:


a = b = c;

Did you try it?

Thomas
 
N

Noah Roberts

Thomas said:
Tomás schrieb:

Did you try it?

Probably not since last I used C that kind of code was rampant. I do
this often:

int a, b, c;

a = b = c = 0;

Of course not exactly in cases like that since it is better to
initialize than assign, even if it requires a few more characters be
typed, but on reassignment that often is done and perfectly valid in C
as well as C++.
 
N

Noah Roberts

Noah Roberts wrote:
Yes. The concept of forcing [][] on an object because it is
implemented with an array is inherently flawed. Try implementing [][]
with a bitboard for example and then compare that implementation to
functional notation.

class Board
{
private:

uint64 white_pieces;
uint64 black_pieces;
uint64 white_knights;
....


? operator[](uint row);

// easier and more appropriate:
piece pieceAt(uint row, uint col);
piece operator()(uint row, uint col);
};

Now, in actually building a chess engine you're probably going to break
encapsulation anyway and create massive dependencies just to speed
things up a hair (if you've never done it let me tell you that a LOT of
time is spent trying to get just a little more oomph out of your
classes) but at any rate this is a perfect example of what happens when
you think, "Hey this is an array, lets make it look like one..." Well,
what happens when you decide it shouldn't be implemented with an array
anymore??

I must say I cannot see your point. The rational of using operator
overloading is not whether the internals of the class are with array or
something else. It is intending to make it look like array.

The point is that in imposing the array semantics on the above board
object you have created a depenency on the internals. It would be
difficult and inefficient to implement that interface with a
bitboard...if you doubt that then try it.

Anyone
using C++ is accustom to using [] for accessing continues memory.

Using objects is about abstraction.

This
is why you have it in std::vector and std::string but you don't at
std::list.

Conceptually both vector and string are arrays. That is the
abstraction they are. Board, Matrix, etc...are all abstractions above
that. You can argue that a 2d array in fact is conceptually an array
and this would be correct but there are two things to consider. First,
implementing that correctly is not as simple as creating some storage
medium and then exposing it to the public. You need abstractions or
you are better off with the more natural primative type itself. The
second thing to consider is that you already have 1d arrays that can,
used together, do everything you might need of a 2, 3, 4, etc dim
array. In an effort to use the simplest method to get the job you need
done why reinvent the wheel for each dimension? Why not use the tools
at your disposal that do the job you need done?

At any rate...Board and Matrix as types or abstractions are NOT arrays.
I gave a perfect example of why pretending they are is a problem...A
board and matrix both have two dimensional coordinates and have an
"area" but they are an abstraction above array and thus shouldn't have
array semantics imposed upon them - they could use an array inside but
shouldn't expose that to the outside in any way, including especially
their own interface. I would argue that the same is true for the OP's
bitmap class.
 
J

Jack Saalweachter

Noah said:
Probably not since last I used C that kind of code was rampant. I do
this often:

int a, b, c;

a = b = c = 0;

Of course not exactly in cases like that since it is better to
initialize than assign, even if it requires a few more characters be
typed, but on reassignment that often is done and perfectly valid in C
as well as C++.
How would L-versus-R-values enter into it?

Because of right associativity, 'a = b = c = 0;' is evaluated as 'a = (b
= (c = 0));' in either language, so regardless of whether assignment
results in an R-value or an L-value, the result of assignment is only
being used as an R-value.


Jack Saalweachter
 
N

Noah Roberts

Jack said:
How would L-versus-R-values enter into it?

Because of right associativity, 'a = b = c = 0;' is evaluated as 'a = (b
= (c = 0));' in either language, so regardless of whether assignment
results in an R-value or an L-value, the result of assignment is only
being used as an R-value.

Yep, that would be the reason why it works just fine.
 
T

Tomás

Jack Saalweachter posted:

How would L-versus-R-values enter into it?

Because of right associativity, 'a = b = c = 0;' is evaluated as 'a = (b
= (c = 0));' in either language, so regardless of whether assignment
results in an R-value or an L-value, the result of assignment is only
being used as an R-value.


Wups, I meant something like:


int main(void)
{
unsigned a = 1, b = 2, c = 3;

(a = b) = c;
}


-Tomás
 
J

Jim Langston

Thomas J. Gritzan said:
The OP seems to operate on a little endian machine.


I hope the code does not depend on it. :)

Yes, it confused the heck out of me. Before I did this I was 100% sure that
Intel was little endian. I was quite suprised of the results of the
program. That's why I did this testing. I'm not sure if the compiler isn't
actually rearranging the order of the R B G A variables, but I didn't think
it could do that. Yet the output is correct. I'm still confused.
 
J

Jim Langston

The reason you can find in MFC class names prefixing with C in their
names is that when Microsoft implemented MFC namespaces were not widely
supported in many compilers. In order to have their class names unique
they used C prefix to all MFC class names. Many inexperience
programmers seeing the practices from Microsoft and following them
blindly. I believe that anyone who is doing those king if thing is
missing a key understanding in using whatever tool he/she is trying to
use.
Basically it's should bather anyone seeing others placing their own
code under namespace std just because the standard library is doing it
too
Hope that now you can understand why I think this is truly a band
practice

The thing is, I'm not using C for classes because MFC does it, I do it as a
simplified form of hungarian notation. It is the only place I still use
hungarian notation as thing it's one place it actually has a use.

I'm one of the programmers that lived through the beginning of hungarian
notation when it was first developed and everyone started using it everyone
and it made code that became horid to read. cpiaElement and such and even
longer ones. I'm glad that HN died, but it still has it's uses.
 

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

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,044
Latest member
RonaldNen

Latest Threads

Top