Operator overloading (was: Re: Several questions on Objective C)
Operator overloading (was: Re: Several questions on Objective C)
- Subject: Operator overloading (was: Re: Several questions on Objective C)
- From: Michael Gersten <email@hidden>
- Date: Mon, 08 Apr 2002 15:17:12 -0700
Wait a day, make three revisions, then post.
Ondra Cada wrote:
>
Well, I've never needed that myself (and thus never done it), but I guess
>
it would be relatively simple to expand the ObjC language by a _specific_
>
set of overloadable operators.
>
>
The idea is quite plain:
>
>
(i) make a preprocessor, which would translate a pattern
>
>
exp @c exp
>
>
(where exp is an ObjC expression, @ is just '@', and c is any single
>
character) to
>
>
[exp operator_G:exp]
>
>
where G is the glyph name of the character c.
I like it. And, it needs a little work.
root=(b^2-4*a*c)/(2*a)
becomes
root = (b @^ 2 @- 4 @* a @* c) @/ (2 @* a)
Becomes
root = [ (
[
[b operator_exponent:
[NSNumber numberWithInt: 2]
]
operator_minus:
[
[[NSNumber numberWithInt: 4] operator_times:
[a operator_times: c]
]
]
)
operator_divide:
(
[[NSNumber numberWithInt: 2] operator_times: a
]
)]
(I hope I used a consistant set of translation rules on that. Done by hand.)
The problem is [[NSNumber numberWithInt: 2] operator_foo: x]. For this example, it commutes; in general, it won't. Think exponent, minus, divide, etc. Think matrix multiplication, etc.
If all of those are of type 'complex', then
(b^2 - a * c * 4) / (a * 2)
works nicely. You are always messaging an object of type complex; it can ask "Is my argument an NSNumber? Do this. Is it a Complex? Do this. Is it something else? Raise an exception -- invalid argument".
But you have to ask 'What is the type of my argument' -- that's an OO nono.
Now the drawbacks for the first concept:
#0. You have to define all of the operator_foo methods on NSNumber.
#1. There's no way in ObjC (that I know of) to use the type of the argument during dispatch -- you have to query the type of argument, and figure out what to do in each case
#2. If the usage is symetrical, then you have to handle both directions. For constants, this is doable, ONCE -- categorize NSNumber, and add the appropriate routines there (Objective C only -- sorry Java). But you can't do that in code that has all of the XXComplex, YYMatrix, and ZZVector bundles.
#3. If I'm mixing frameworks/bundles, then the mixed cases become a pain. In C++, if I'm using both XXComplex and YYMatrix, I can define what it means to multiply a complex and a matrix -- the specification will be unique to what I add, and I know what code will be called. (The implementation isn't difficult -- each thingy in the matrix that is an NSNumber and not an XXComplex is converted to an XXComplex. Then, regular multiplication occurs, with the XXComplex::operator* (XXComplex) routine being called for each multiplication.)
[Now, APL fans will complain that I'm not allowing the elements of the matrix to be something other than a number, or a complex -- an element might itself be a vector or a matrix. A matrix, after all, is just a two-dimensional, structured/ordered collection, right?]
In ObjC? Near as I can tell, I'd have to poseAs: on each of the bundles I'm using, and do all the "What is my arg?" processing.
Now, for the refinements/solutions:
Instead of [class1 operator_foo: class2]
How about [class1 operator_foo_class_bar: class2]
where bar is what the compiler knows the type of class2 to be?
Or, [class1 operator_foo_protocol_pee: class2] when class2 is defined to be a protocol instead of a class?
(rememind me: Does ObjC allow an object to be defined as multiple protocols, or only one protocol?)
Suddenly, simple categories solve everything, even multiple things in NSNumber, mixing different bundles, etc.
You still need the preprocessor to generate operator_foo methods.
And, an -[NSObject operator_foo:] method, that actually hunts down the type of the second argument, and looks backwards through the parenting hirarchy of the second argument until it finds one that self responds to (in the second form, where the type of the second argument is in the method name), and then calls it. Otherwise, you can't subclass the second argument.
{{ Hmm... do protocols inherit? All these things I've never though of about them before now. }}
Ok, now for revision three. At compile time, the compiler can ask, "I only know the protocol of this class, do I need to generate an operator_foo_protocol_bar:, or an operator_foo_class_bar:?". And, if something is defined by a protocol, you might.
At run time, you know the class of an object, not if it was declared by protocol, nor even what protocols it follows. At run time, operator_foo: can only determine the class, and call operator_foo_class_bar.
Will this cause problems for things defined by protocol instead of by class?
If you only have a protocol, and an operator_foo_protocol_bar: message, can you tell where to put the method?
As far as I can tell, you have to go the class route, and lose any protocol-supplied information. But if you are working with a hidden class -- where you only have the protocol information, and no access to the class information -- will this work well enough? It can only work by adding a category to NSObject -- assuming this hidden class either descends from NSObject, or looks like it decends from NSObject (such as a remote proxy). And, if you don't know the class of the argument, suddenly you're overloading names in NSObject. Foo, this fails.
So we need the preprocessor to generate both operatorClass_minus: methods for things that are defined by a class, which in turn looks through the inheritence hierarchy of argument 2 for a operator_minus_class_NSNumber: that the first argument will respond to, and we need an operatorProtocol:minus: method that does the same thing, only with an explicit protocol name. If this can't find any operator_minus_protocol_NumberProtocol method, then it asks operatorClass_minus: to try based on class info instead of protocol info.
The only remaining drawback is that the NSObject umbrella re-dispatching methods have the operator name in the method. It makes more sense to just have
- operatorOverload: (NSString *) operator argument: (id) argument
- operatorOverload: (NSString *) operator argument: (id) argument protocol: (NSString *) protocol
as the two generic NSObject-located redispatchers.
Michael
--
I am a Cocoa/WOF/EOF developer, and it looks like I'll be availble for hire. Please contact me at michael-job @ stb.nccom.com if interested.
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.