Message forwarding in Smalltalk - oop

So I'm writing an application where one object has a bunch of delegate objects that it forwards messages to. The idea is that I can say
someObject sendMessage:aMessage
and aMessage will be sent to all of someObject's delegates (for any value of aMessage). The only way I've been able to do this is something like:
sendMessage:aMessage
| sel chunks kwords arglist msg |
chunks := aMessage findTokens:' '.
kwords := Array new:(chunks size).
arglist := Array new:(chunks size).
1 to: (chunks size) do: [:i |
kwords at:i put:((chunks at:i) findTokens:':') at:1.
arglist at:i put:((chunks at:i) findTokens:':') at:2].
sel := ''.
kwords do:[:word | sel := sel,word,':'].
msg := Message selector:sel arguments:arglist.
delegates do:[:del | del perform:msg selector with:msg arguments].
It works, but there has to be a better way. This solution limits the arguments to being strings, and is just plain ugly. Does anyone know a cleaner, better way to forward messages?
BTW, I'm using squeak, but an implementation-independent solution would be prefered ;)
EDIT: I should add that the delegates are of the same class as the object, so I can't just override DoesNotUnderstand:.

Since you want to pass objects in as arguments, you'll have to pass them in as a separate list of using a message pattern like the following:
someObject sendMessage: aSelector withArguments: argumentList
Then you'd implement #sendMessage:withArguments: as:
sendMessage: aSelector withArguments: argumentList
delegates do:[:del |
del perform: aSelector withArguments: :argumentList].
and you'd be able to forward arbitrarily complex messages using real objects as args:
| arguments |
arguments := Array
with: Object new
with: 1234.5
with: ('key'->'value').
someObject sendMessage: #foo:bar:baz: withArguments: arguments
I think this is portable to most dialects as well...

Try implementing this (it will only forward messages that aren't understood by the object that has the delegates):
doesNotUnderstand: aMessage
delegates
do: [:delegate | aMessage sendTo: delegate]
You could construct Message objects explicitly like:
msg := Message selector: #foo arguments: #(bar baz)
"then use them like:"
anObject perform: msg selector with: msg arguments

In Squeak, see the class ObjectTracer. You can use it to intercept all message sends to an Object.

Well, without knowing what aMessage is, and since you mentioned all your delegate objects are of the same class, I'd do something like:
MyobjectClass>>SendMessage: aMessage
self doSomethingUsefulOnThisInstanceIfApplicable: aMessage.
self dependents do: [:ea | ea SendMessage: aMessage ] .
You may also want to look to see if using any of the following messages could work for you: (these are from Cincom VisualWORKS)
update:
update:with:
update:with:from:

Why not simply use a polymorphism, that is, implement this method in class of each object you are calling? Then you implement in your object method with the same name, which just delegates a call to all subobjects. Something like:
MyObjectClass>>someMethod
subobjects do: [:each | each someMethod]

Related

Class as parameter in Smalltalk

I need to know if it is possible to pass a Class as a parameter to a method in Smalltalk. For example,
Classname>>method: anObject
self anotherMethod: aClass with: anObject.
Classname>>anotherMethod: aClass with: anObject.
|instance|
instance:= aClass new: anObject aMessage. //supposing "new:" is the instance method of aClass.
instance aMethodFromTheClassRecieved.
Yes. Classes are just objects.
If you have:
Classname>>anotherMethod: aClass
^ aClass new.
and you execute something like:
instance anotherMethod: OrderedCollection
Then you'll get an instance of OrderedCollection.
In Smalltalk classes are objects too, so if you do OrderedCollection new you actually sent #new message to OrderedCollection class object. So you can pass classes around just like the other objects.
P.S. The main idea of Smalltalk is that it's highly dynamic and live. You can try the thing you are asking about in just 2-5 minutes, and see if it works :)

Squeak Smalltalk: initialize don't work for class derived from Array?

I'm a bit of a novice when it comes to Squeak Smalltalk, so I'll probably done something wrong or made an erroneous assumption about how Squeak should work. Still, I'd like to know where I went wrong...
I tried to make class Blower derived from Array. Blower is basically an Array, but with an additional instance variable called index and some methods. I thought the initialize method would be automatically run when I created a new object, and that it would initialize both the array and the index variable, but this doesn't seem to happen. If I run initialize "manually" later though, it works as expected.
Array variableSubclass: #Blower
instanceVariableNames: 'index'
(...)
Blower >> initialize
super initialize.
1 to: self size do: [ :ix | self at: ix put: ix ].
self shuffle.
index := 1.
If I do the following in a workspace:
blower := Blower new: 10.
blower inspect.
Inspect-window shows (Not what I expected):
\#( nil nil nil nil nil nil nil nil nil nil )
index: nil
If I run initialize manually, Inspect-window is correct:
blower initialize.
\#( 6 4 1 10 2 8 3 ... )
index: nil
So why doesn't initialize run when I create the Blower and set it up correctly?
Is there anyway to automate this so it happens on creation? Ie. getting initialize to work?
Look at the method ArrayedCollection class >> new. It overrides new to call new: with 0 as a parameter. This replaces the default implementation of new in Behavior which calls initialize. If you really want to do this, implement new and new: as class methods in your class. In each case, call super then call initialize.
new
^super new initialize
new: sizeRequested
^(super new: sizeRequested) initialize
Having said all that, it's a really bad idea to subclass from Array. Ask yourself "Is it reasonable for me to use a Blower any place I currently use an Array?". If not, it's not a good subclass. Any time you subclass from a collections class you're almost always doing it wrong. What you want is a class called Blower which is a subclass of Object and contains two instance variables - one for the array and one for the index. Your class will now initialize normally. For any operations you want to send to the array, write a method in the Blower to delegate it to the instance variable.
What David Buck said is correct, but there is a few more things to add, specific to Squeak:
Some collections invoke #initialize: instead of #initialize at instance creation (see for example HashedCollection)
Some other could send both #initialize then #initialize: (see SharedQueue)
But Array class>>new: has a specific implementation that completely bypasses initialize (for the sake of speed, it is known that there is nothing required for initializing an Array)
As David said, it is generally a bad idea to subclass Array, and looking at Squeak, there are too many counter-examples already.

Are selector IDs sensitive to argument types?

When you retrieve the ID of a selector with #selector(), is the selector value different depending on the types of the arguments?
Here's what I mean. I have a method that takes an object reference and a selector, then calls it with a parameter:
-(void)CallLater:(NSObject*) Obj Sel: (SEL)Sel
{
//Some stuff, then:
[Obj performSelector: Sel withObject: SomeOtherObject];
}
I'm using this method with a selector to a function that takes a typed object reference, not an id:
-(void)MyMethod: (MyObject*) a
{
}
[self CallLater: self Sel:#selector(MyMethod:)];
It seems to work, but my senses are tingling. In a statically typed language like C# this would be a foul, an upcast - CallLater is expecting a selector for a function that takes an id and I'm giving it a function that takes a MyObject.
On the other hand, the compiler does not complain, and both id and concrete object references seems to be mere pointers deep down, trivially castable to one another. Then again, there are many fouls that Objective C compiler does not complain about.
So the real question is - is it safe? Language lawyers welcome.
It's safe; objects are objects. A selector parameter for an NSObject * is exactly the same as a selector parameter for a MyObject *.
If you want MyMethod to verify that it's being called with an object of a particular type, it should do a NSParameterAssert on it:
NSParameterAssert([obj isKindOfClass: [MyObject class]]);
Personally, I rarely do this check. It's enough that the actual object acts like the type I want it to be, and if it doesn't I'll get a runtime error (usually unrecognized selector). You'll get a compiler warning in the simple cases, and it's worth paying attention to this warning (and silencing it with an id cast when necessary).
I'm a bit confused here about your use of id in your question, so I want to make sure you understand this: An NSObject * is exactly as much an id as a MyObject * is. id is a generic instance pointer class, whereas NSObject * is a NSObject instance (or a subclass of NSObject). You can have objects that don't descend from NSObject. But you're unlikely to ever have to know this.
Other notes, re: convention:
Selectors (both the name and parameters) start with lowercase letters, so CallLater:Sel: should be callLater:sel:.
Variable and parameter names start with lowercase letters; Obj above should be obj.
Class names do start with an uppercase letter. :)

Objective-C Selector pointer to be passed to a C function

I have a C struct that contains a function pointer. Now, I have used this setup within C with no problems, but now I'm using this C struct in Objective-C and I need to pass a function (or selector) pointer that is defined in the Objective-C class.
1. Here is what I have for the Objective-C selector that needs to be passed as a pointer to the C function:
- (void)myObjCSelector:(int*)myIntArray
{
// Do whatever I need with myIntArray
}
2. And here is where I run into a wall, Within Objective-C I'm trying to pass the selector as a pointer to the C function call: In place of "myObjCSelectorPointer" I need the proper syntax to pass the selector as a function pointer in this C function call:
passObjCSelectorPointerToCContext(cContextReference, myObjCSelectorPointer);
I did investigate this issue, but could mainly find several different ways of doing similar things, but I couldn't find anything specific for calling C functions and passing an Objective-C selector pointer.
In objc a selector is not a function pointer. A selector is a unique integer that is mapped to a string in a method lookup table stored by the objc runtime. In the above case your method name would be myObjCSelector: and to get the unique selector for it you would type #selector(myObjCSelector:). However this would be of no use to you because it doesnt represent a particular implementation of a function.
What youre looking for is IMP. Refer to this SO question.
EDIT 2:
IMP myObjCSelectorPointer = (void (*)(id,SEL,int*))[self methodForSelector:#selector(myObjCSelector:)];
Then you can call the method using
myObjCSelectorPointer(self,#selector(myObjCSelector:),myIntArray);
However, what this means you will need to make sure that you add the pointer to self in the c function call passObjCSelectorPointerToCContext.
So it should look like this
passObjCSelectorPointerToCContext(cContextReference, self, myObjCSelectorPointer);
when called from within the object that contains the method.
It is important to note though that using IMP is almost never the right technique. You should try to stick with pure Obj-C. Obj-C is quite efficient after the first call to a message because it uses temporal caching.
EDIT 1:
It's useful to understand why objc works in this way. The Apple documents explain it in depth. However a short explanation is as follows:
When you send a message to an object such as [myobject somemethod] the compiler won't immediately know which particular implementation of somemethod to call because there might be multiple classes with multiple overriden versions of somemethod. All of those methods have the same selector, irrespective of its arguments and return values and hence the decision about which implementation of somemethod is deffered to when the program is running. [myobject somemethod] gets converted by the compiler into a C function call:
objc_msgSend(myobject, #selector(somemethod))
This is a special function that searches each myobject class layout to see whether that class knows how to respond to a somemethod message. If not it then searches that class's parent and so on until the root. If none of the classes can respond to somemethod then NSObject defines a private method called forward where all unknown messages are sent.
Assuming that a class can respond to the somemethod message then it will also have a particular pointer of type IMP that points to the actual implementation of the method. At that point the method will be called.
There is considerably more to this procedure than I have described but the outline should be enough to help you understand what the goal of a selector is.
One final point is that the reason method names are mapped to unique integers via the #selector directive is so that the runtime doesn't have to waste time doing string comparisons.
Basically, the answer is: Objective-C selectors are different from function pointers. You need two pieces of data to perform a selector. That is an object and the selector itself. You will need some glue to accomplish your task.
Check this question.
Do you have to use a function pointer? In Objective-C, you can get the function pointer to an arbitrary method implementation (known as an IMP), but this is extremely uncommon, and usually not a good idea. Calling objc_msgSend() directly is also not the greatest idea, because there are several different variants of objc_msgSend(), and the compiler automatically chooses different ones to use based on the return type of the method. Methods that return an object go through objc_msgSend(), but objects that return structs might go through objc_msgSend() or they might go through objc_msgSend_stret(). And if the method returns a double, then it goes through objc_msgSend_fpret()...
Documentation: Objective-C Runtime Reference: Sending Messages
Instead, I might recommend using a target-action pair, or using a block. Then you might do something like:
myContextRef->target = anObjcObject;
myContextRef->action = #selector(invokeMe:);
And when you're done, do:
[myContextRef->target performSelector:myContextRef->action withObject:someReturnInformation];
Or maybe use a block:
myContextRef->completionHandler = [^(id returnInformation) {
[anObjcObject invokeMe:returnInformation];
} copy];
And then when you're done, do:
myContextRef->completionHandler(someReturnInformation);
(and don't forget to -release the block when you free the context)

Objective-C va_list and selectors

Is it possible to use #selector and performSelector: (or similar) with methods using variable arguments list?
I'm writing a class that can be assigned a delegate to override the default behavior. In the presence of a delegate select method calls made on an instance of that class will be forward to the same corresponding delegate method, some which use variable argument lists.
So, for instance, I need to be able to create retrieve SEL reference and message the delegate object with a method such as this:
- (void)logEventWithFormat:(NSString *)format, ... {
va_list argList;
id del = self.delegate;
if (del != nil &&
[del conformsToProtocol:#protocol(AProtocolWithOptionalMethods)] &&
[del respondsToSelector:#selector(logEventWithFormat:)])
{
// Perform selector on object 'del' with 'argList'
}
}
I am assuming this is not possible, hence the similar method declaration in the Foundation framework - in NSString:
- (id)initWithFormat:(NSString*)format, ...;
and
- (id)initWithFormat:(NSString *)format arguments:(va_list)argList;
I assume that the protocol I wish to delegate to should suggest the implementation of:
- (void)logEventWithFormat:(NSString *)format arguments:(va_list)argList;
so I the selector #selector(logEventWithFormat:arguments:) can be used an called with:
[del performSelector:#selector(logEventWithFormat:arguments:)
withObject:format
withObject:argList];
I just wondered if I was missing something or going the long way around to achieve what I'm trying to?
You can pass anything you want into the runtime function objc_msgSend.
objc_msgSend(del, #selector(logEventWithFormat:arguments:), format, argList);
It's the most powerful way of sending a manually constructed message.
However, it's not clear that you need to perform the invocation this way. As KennyTM pointed out, in the code you have, you could invoke the method directly.
You can't use -performSelector:withObject:withObject: because va_list simply isn't an "object". You need to use NSInvocation.
Or simply call
[del logEventWithFormat:format arguments:argList];
As far as I know, it can't be done. You can't use -performSelector:withObject:withObject: because as #KennyTM points out, a va_list isn't an object.
However, you also cannot use NSInvocation. The documentation straight up says so:
NSInvocation does not support
invocations of methods with either
variable numbers of arguments or union
arguments.
Since these are the two ways of invoking a method by selector, and neither seems to work, I'm going to go with the "can't be done" answer, unless you invoke the method directly and pass the va_list as an argument.
Perhaps #bbum will show up and enlighten us further. =)
I haven't done it that way before, but the simple solution I've often used is to box/unbox either an NSMutableArray or an NSMutableDictionary for the withObject parameter.