[comp.lang.c++] Extensions to

powell@ole.UUCP (Gary Powell) (06/06/90)

I am new to C++ but after a seminar and some play time I tried writting
a class for two diminsional arrays.  What I found myself wanting was
a overloaded operator [][](int)(int).  However my GNU g++ 1.35 compiler
complains bitterly when I tried to code this.  Why not use the (int,int)
operator? Well I was using that for the initialization. It would seem to
me that since C allows array[][] access that C++ should allow overloading
of this also.

With this background has there been any discussion on the overloading of
[][] ....[]?

And is this a reasonable extension to the language?

Resons for doing this are that N'dim. arrays are a pain to pass
to functions.  With a class I can add the extra information for bounds
checking and pass the arguement as a single pointer and let the compiler
call my code to access the array.

class TWO_DIM {
public:
    TWO_DIM(int r,int c) { /* initialize  */}
    int & operator[][](int r)(int c) { /* bound check */ return & array [r][c];}
    ~TWO_DIM() {delete array;}
private:
    int *array,
	max_col,
	max_row;
};

subroutine (TWO_DIM &i-arry)
{
    i-arry[5][2] = i-arry[2][5];   // garbage code.
}
main()
{
    TWO_DIM i-array(10,20);

    i-array[0][15] = 5;

    subroutine (i-array);
}
-- 
   _Q   _Q    _Q     _Q  _Q_Q    _Q    _Q                                    _Q
  /_\) /_\)  /_\)   /_\)/_/\\)  /_\)  /_\)                    Gary Powell   /_\)
_O|/O_O|/O__O|/O___O|/O_OO|/O__O|/O__O|/O__________________________________O|/O_
uunet!uw-beaver!sumax!ole!powell            Seattle Silicon Corp. (206) 828-4422

keith@csli.Stanford.EDU (Keith Nishihara) (06/07/90)

In <1589@ole.UUCP> powell@ole.UUCP (Gary Powell) writes:

>[...] What I found myself wanting was
>a overloaded operator [][](int)(int).  However my GNU g++ 1.35 compiler
>complains bitterly when I tried to code this.

You can do it in the obvious way using two applications of operator[].
If you want to do checking or other additional functions on both indeces,
you will need to define a half way class which is returned from the
first application of operator[], and which serves simply as a base
for the second operator[].

For example:
#include <stdio.h>

// Declarations.
class A;
class Half;

// Define class A which is a 2D array.
class A
{
public:
    A();
    Half &      operator[] (int);
private:
friend class Half;
    int         x;
    int         y;
    int         *data;
};

// Constructor simply sets up a dummy array and initialises it.
A::A()
{
    x = 5;
    y = 10;
    data = new int[x * y];
    for(int j = 0; j < x * y; j++)
        data[j] = j;
}

// Define class Half which is a placeholder for the half decoded array access.
class Half
{
public:
    int         operator[] (int);
private:
friend class A;
    int *       a;                      // half decoded access.
    A *         from;                   // pointer to original array.
};

// First half of array access.
Half &
A::operator[] (int j)
{
    printf("A::operator[%d]\n", j);

    static Half h;

    if(j < 0)           // Range checking.
        j = 0;
    if(j >= y)
        j = y-1;

    h.a = data + j*x;   // Part access.

    h.from = this;      // So that Half can get to array dimensions.

    return h;           // Return reference to local static Half.
}

// Second half of array access.
int
Half::operator[] (int i)
{
    printf("Half::operator[%d]\n", i);

    if(i < 0)           // Range checking.
        i = 0;
    if(i >= from->x)
        i = from->x - 1;

    return a[i];        // Rest of access.
}

main()
{
    A array;

    printf("Normal access: %d\n\n", array[2][3]);
    printf("Out of bounds in first half: %d\n\n", array[20][3]);
    printf("Out of bounds in second half: %d\n\n", array[2][30]);
}

// Gives results:
//
// A::operator[2]
// Half::operator[3]
// Normal access: 13
// 
// A::operator[20]
// Half::operator[3]
// Out of bounds in first half: 48
// 
// A::operator[2]
// Half::operator[30]
// Out of bounds in second half: 14

Neil/.		Neil%teleos.com@ai.sri.com

tmb@lem.ai.mit.edu (Thomas M. Breuel) (06/08/90)

In article <1589@ole.UUCP>, powell@ole.UUCP (Gary Powell) writes:
|>I am new to C++ but after a seminar and some play time I tried writting
|>a class for two diminsional arrays.  What I found myself wanting was
|>a overloaded operator [][](int)(int).  However my GNU g++ 1.35 compiler
|>complains bitterly when I tried to code this.  Why not use the (int,int)
|>operator? Well I was using that for the initialization. It would seem to
|>me that since C allows array[][] access that C++ should allow overloading
|>of this also.
|>
|>With this background has there been any discussion on the overloading of
|>[][] ....[]?
|>
|>And is this a reasonable extension to the language?
|>
|>Resons for doing this are that N'dim. arrays are a pain to pass
|>to functions.  With a class I can add the extra information for bounds
|>checking and pass the arguement as a single pointer and let the compiler
|>call my code to access the array.

Overloading "operator[][]" is not really the right thing to do--
there is no such thing as an "operator[][]".

Probably the best solution is to break with tradition and
make "operator[]" completely analogous to "operator()" in
the treatment of "multiple arguments" and commas. This would
constitute a minor incompatiblity with ANSI C, since
ANSI a[1,2] would now have to be written a[(1,2)], but
this should not affect a lot of code, and it should be easy
to fix where it occurs.

An ANSI C compatible solution to the problem would be to treat
"operator[]" analogous to "operator()" iff it is used with
a non-pointer type.

If someone implemented this in GNU C++, I'm sure it would 
catch on... it is simply a very useful feature.

					Thomas

PS: The popular approach to "overloading operator[][]" is
to have the first operator[] return a "partially dereferenced result",
and have the second operator[] complete the job:

struct floatArray2d = {
	...
	partiallyDereferencedFloatArray2D operator[](int) { ... }
};

struct partiallyDereferencedArray2D = {
	floatArray2D array;
	int index1;
	float operator[](int) { ... }
};

This is, of course, undesirably complex and messy.
Incidentally, the same kludge is used for defining the equivalent
of CommonLisp SETF methods, and, again, a simple mechanism would
be desirable.

jimad@microsoft.UUCP (Jim ADCOCK) (06/09/90)

In article <1589@ole.UUCP> powell@ole.UUCP (Gary Powell) writes:
>I am new to C++ but after a seminar and some play time I tried writting
>a class for two diminsional arrays.  What I found myself wanting was
>a overloaded operator [][](int)(int).  However my GNU g++ 1.35 compiler
>complains bitterly when I tried to code this.  Why not use the (int,int)
>operator? Well I was using that for the initialization. It would seem to
>me that since C allows array[][] access that C++ should allow overloading
>of this also.
>
>With this background has there been any discussion on the overloading of
>[][] ....[]?
>
>And is this a reasonable extension to the language?

No, it shouldn't be necessary, and [][] is not one operator but two.  Changing
[][] from two operators to one would significantly change C++ from the C past.
And doing so shouldn't be necessary.

There are two common tricks used by people doing 2-dim [or n-dim] arrays
in C++.  Examples follow.  The first trick is to build up your objects
from rows and columns as the two [] operators implies.  The second trick is
to return a proxy object from the first application of [], that "remembers"
where you are in the 2-dim [n-dim] array after the first application, and
then the second application of [] is applied to the proxy object, which 
"remembers" the previous index, and thus has all the information necessary
to return the correct element in the 2-dim [n-dim] array.  BUT: be forwarned
there are a fair amount of C and/or C++ compilers out there that have a 
hard time correctly compiling the code necessary to do proxies!


// Example of building up a two dimemsional array one dimension at a time:


extern "C"
{
#include "stdio.h"
}


class COL
{
public:
	int c[10];
	int& operator[](int i) { printf("COL[%d] ",i); return c[i]; }
};

class MAT
{
public:
	COL r[10];
	COL& operator[](int j) { printf("ROW[%d] ",j); return r[j]; }
};

main()
{
	MAT mat;

	mat[5][6] = 56;
	printf("\n");
	mat[6][5] = 65;
	printf("\n");

	printf("mat[5][6] %d\n", mat[5][6]);
	printf("mat[6][5] %d\n", mat[6][5]);
}

// example of using proxies:


extern "C"
{
#include "stdio.h"
}

#include "shortnew.h"

typedef int* PI;

class PROXY
{
	const PI c;
public:
	PROXY(const PI cc) : c(cc) {}
	int& operator[](int i) const { printf("COL[%d] ",i); return c[i]; }
};

class MAT
{
	int i[100];
public:
	PROXY operator[](int j) const 
	{ 
		printf("ROW[%d] ",i); 
		return &(i[j*10]); 
	}
};

main()
{
	MAT mat;

	mat[5][6] = 56;
	printf("\n");
	mat[6][5] = 65;
	printf("\n");

	printf("mat[5][6] %d\n", mat[5][6]);
	printf("mat[6][5] %d\n", mat[6][5]);
}

diamond@tkou02.enet.dec.com (diamond@tkovoa) (06/11/90)

In article <8903@life.ai.mit.edu> tmb@ai.mit.edu writes:

>Overloading "operator[][]" is not really the right thing to do--
>there is no such thing as an "operator[][]".

Syntactically, [][] has not been specified as a single operator,
but it is possible to do so.  (Semantically, it has already been done:
 char *x[10];  char y[10][20];
Then   z = x[5][6];   generates different code from   z = y[5][6];
but this could not be done if [][] were really two separate operators.)

>Probably the best solution is to break with tradition and
>make "operator[]" completely analogous to "operator()" in
>the treatment of "multiple arguments" and commas. This would
>constitute a minor incompatiblity with ANSI C, since
>ANSI a[1,2] would now have to be written a[(1,2)], but
>this should not affect a lot of code, and it should be easy
>to fix where it occurs.

Exactly.  A couple of years ago, I pointed out that the existing
semantics are already somewhat inconsistent.  In function calls and
initializers, if one wants the comma operator instead of just a
separator, then one has to use an extra set of parentheses.

Theoretically, such a change could break existing code.  Anyone want
to bet that any real, serious code, outside of compiler test suites,
really uses the comma operator inside a pair of square brackets?

-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
Proposed group comp.networks.load-reduction:  send your "yes" vote to /dev/null.