Re: Operator Overloading
Stefano Corsi <[email protected]> Tue, 15 Apr 2003 10:21:08 +0000
| Newsgroups | gmane.comp.lang.moto.devel |
|---|---|
| Organization | Moto Project |
| Message-ID | <[email protected]> |
> > in reality, I've implemented only +=3D and -=3D, so a working example
> > could be:
> >
> > ${
> > use "codex.util";
> > IntSet is =3D new IntSet();
> > is+=3D1;
> > print is.size() + "\n";
> > }$
>
> Interesting, I saw the .i file for Inset. I wonder though if what
> should be returned from the result of +=3D is a brand new (cloned) Ints=
et.
It seems strange to me that ... adding something to something could give =
a new=20
something (or maybe not: is it a metaphore of life?).=20
Maybe I'm wrong but I've always imagined overl. ops as a more intuitive w=
ay to=20
specify methods like _add, _delete, _get, and so on. And if I define an _=
add=20
method for an object I'd use it to add a value to THIS object, not some n=
ew=20
(cloned) object.=20
[ See below ]
> > if (v1->type->kind !=3D INT32_TYPE && v1->type->kind !=3D INT64_TYPE)=
{
> > moto_illegalTypeForArrayIndex(v1->type->name);
> > }
> >
> > this would throw an error in case of [] operators used with string
> > indexes
> > (ex. SymbolTables: foo["bar"]). What kind of strategy should we use
> > here? Get
> > rid of the check? :)
>
> The [] operator is a tough case ... probably the toughest. We may need
> to factor motov_array_index function back into the motov_array_rval
> code. In general what we want to do is, prior to the default type
> check, see if there a function / method for the specified operator that
> matches the operands past by calling motox_lookupMethodOrFn and seeing
> if it returns anything
I agree: we should grab the ops, check if the overl. op exists and eventu=
ally=20
execute the function.
> > 2) [MEDIUM] I find it redundant to write code for every operator. But=
,
> > on the
> > other side, I haven't found a good and elegant way to create a unifie=
d
> > function, something like
> > see_if_you_find_and_operator_for_this_unioncell_execute_and_create_th=
e_
> > result(),
> > because every operator has its own semantic meaning and its own
> > behaviour.
>
> What I did in motoi (so far) is factor the code you wrote out of
> motoi_assign into a function
>
> static int
> motoi_try_overloaded_binary_op(int op,MotoVal* v1, MotoVal* v2)
>=20
> I figure we could have a motoi_try_overloaded_unary_op also or just
> switch to a unary check if the second op is null (or based on the op
> passed in
> I made it return the ftable code for now ... we could change that ...
> so the refactored motoi_assign looks like:
>
> ...
> =09=09=09=09v1 =3D opstack_pop(env);
> =09=09=09=09v2 =3D opstack_pop(env);
>
> =09=09=09=09if(motoi_try_overloaded_binary_op(op,v1,v2) !=3D MOTO_OK) {
> =09=09=09=09=09motoi_domath(v1, v2, op);
> =09=09=09=09=09moto_freeVal(env,v1);
> =09=09=09=09=09moto_freeVal(env,v2);
> =09=09=09=09}
> ...
>
Yes, this is good! We have probably to make some acrobatics in=20
motoX_array_rval and similar, where first one value is popped from the st=
ack=20
and THEN, after many operations, the other values (array indexes) are pop=
ped=20
from the stack. But we can't wait the end of the function, when all value=
s=20
have been popped, to call motoi_try_overloaded_unary_op...
> > 3) [HIGH] What if someone defines an operator with an uncorrect
> > function. I
> > don't know where is the right place to catch this and signal it to th=
e
> > user.
> > For example, imagine this:
> >
> > boolean IntSet::-=3D(int i, int i2, int i3, int i4) =3D>
> > int iset_remove(IntSet *this, int i, int i2, int i3, int i4);
> >
> > here the -=3D operator expects exact one parameter, but we give four =
to
> > it.
>
> Well ... the right place is definitely in mxc somewhere ... but I'm not
> sure offhand where either :) I'll look into this
At the moment I identify an operator in mx.y with:
| m_operator_declaration MAP c_function_declaration SEMICOLON
{
$$ =3D op(MAP, 2, $1, $3);
}
;
we could modify the c_parameter_type_list nonterminal for operators so th=
at we=20
limit the number of arguments taken.=20
C++ checks number of arguments for the operators at compile time:
char * operator<(int a,int b) { return 0; };
pippo.cc:2: `pippo::operator< (int, int)' must take exactly one
argument
but as you can see does not check return type, and let an < operator retu=
rn=20
char *, that is probably meaningless.
> > 4) [HIGH] For "method" overloaded operators (like for example +=3D), =
or
> > in other
> > words operators that take the object itself as parameter, there is a
> > contrast
> > between what the "normal" operator (+=3D) expects on the stack and wh=
at
> > is left
> > from the "overloaded" operator.
>
> Is there ? should there be :) ?
>
> > For example:
> >
> > int a;
> > a +=3D 2 means: take the value of a, add 2 to it and put the result i=
n a.
> >
> > but
> >
> > Intset is =3D new IntSet();
> > is+=3D2 means: perform the _add function on object "is", ignore the v=
oid
> > result.
>
> With the C++ STL I believe the collection itself is cloned and the
> clone is specifically returned by the overloaded operator. While this
> isn't efficient I do believe it is the more correct (safer) way to do
> things (for example we do not re-alloc strings when we use +=3D to add
> them). Thus in C++ at least the +=3D operator for IntSet does return
> something.
I understand now. So, the coherent approach is to create a new value (the=
same=20
operation we do for ints, strings, etc...).
> I do not believe the overloaded function or method in the above case
> should return void. If it is defined as such in the .i file, and the
> void value is used in moto code, a verifier error should be thrown
> saying 'void value not ignored as it should be' . We should not attempt
> to read extension authors minds :) If they say an operator should
> return void we should let it although we should push a void typed
> expression onto the stack.
Ok. We could check number of parameters at the grammar level in mx.y and =
then=20
check type and existence of the return type in motov.c. And use whatever=20
value he states for the return type. But what about < and > and =3D=3D an=
d !=3D.=20
Shouldn't we force the programmer to return "bool"?
> > 5) [HIGH] What do we do with nested operators? For example, suppose w=
e
> > have an
> > [] operator in SymbolTable.i for _get operation. We can write:
> >
> > print <String> foo["bar"];
> >
> > But what if foo["bar"] is a SymbolTable? Should we be able to write:
> >
> > print <String> foo["bar"]["baz"];
>
> Of course :) Unfortunately we may have to write
>
> print <String>(<SymbolTable>(foo["bar"]))["baz"];
>
> since the return type of the overloaded [] operator will be Object.
Is there any way to get rid of the casts? Or are they meant to exist?
> > 6) [LOW] I have a doubt: why in motov.c motov_array_lval just calls
> > motov_array_rval, while in motoi.c it happens the opposite? Could you
> > explain the relationship between the two functions?
>
> In motov it could be either way, the logic in both is the same, for
> either the LValue or the RValue all we care about is the dereferenced
> variable's type
>
> In motoi it is easier to make moto_array_rval depend on moto_array_lval
> because moto_array_lval pushes an address onto the stack. From an
> address you can always get a value for that address. But given a value
> you cannot find out where in memory that value is stored which we would
> need to do for LValues.
>
> So probably motov should be changed to look more like motoi :)
Yes, it 's always better if the two like each other.
I will checkout your new branch!
Stefano