Respond to an unknown method call
You are encouraged to solve this task according to the task description, using any language you may know.
Demonstrate how to make the object respond (sensibly/usefully) to an invocation of a method on it that it does not support through its class definitions. Note that this is not the same as just invoking a defined method whose name is given dynamically; the method named at the point of invocation must not be defined.
This task is intended only for object systems that use a dynamic dispatch mechanism without static checking.
Common Lisp
In Common Lisp, if a generic function is invoked on arguments for which there is no applicable specialized method, the method no-applicable-method
is called with the generic function and the arguments.
<lang lisp>(defgeneric do-something (thing)
(:documentation "Do something to thing."))
(defmethod no-applicable-method ((method (eql #'do-something)) &rest args)
(format nil "No method for ~w on ~w." method args))
(defmethod do-something ((thing (eql 3)))
(format nil "Do something to ~w." thing))</lang>
Evaluating<lang lisp>(list (do-something 3) (do-something 4))</lang> produces
<lang lisp>("Do something to 3."
"No method for #<STANDARD-GENERIC-FUNCTION DO-SOMETHING 214FC042> on (4).")</lang>
E
In E, a message consists of a verb (arbitrary string) and arguments (sequence of arbitrary objects). It is conceptually entirely up to any given object how it dispatches incoming messages.
Practically, the object definition syntax provides a matcher clause to handle unrecognized messages. This example has the same behavior as the Python example.
<lang e>def example {
to foo() { println("this is foo") } to bar() { println("this is bar") } match [verb, args] { println(`got unrecognized message $verb`) if (args.size() > 0) { println(`it had arguments: $args`) } }
}</lang>
JavaScript
There is no standard way to do this, but some implementations provide suitable traps.
<lang javascript>var example = new Object; example.foo = function () {
alert("this is foo");
} example.bar = function () {
alert("this is bar");
} example.__noSuchMethod__ = function (id, args) {
alert("tried to handle unknown method " + id); if (args.length != 0) alert("it had arguments: " + args);
}
example.foo(); // alerts "this is foo" example.bar(); // alerts "this is bar" example.grill(); // alerts "tried to handle unknown method grill" example.ding("dong"); // alerts "tried to handle unknown method ding"
// alerts "it had arguments: dong</lang>
Objective-C
-forwardInvocation:
is usually used to "forward" the message on to another object to handle.
<lang objc>#include <Foundation/Foundation.h>
// The methods need to be declared somewhere @interface Dummy : NSObject { } - (void)grill; - (void)ding:(NSString *)s; @end
@interface Example : NSObject { } - (void)foo; - (void)bar; @end
@implementation Example - (void)foo {
NSLog(@"this is foo");
} - (void)bar {
NSLog(@"this is bar");
} - (void)forwardInvocation:(NSInvocation *)inv {
NSLog(@"tried to handle unknown method %@", NSStringFromSelector([inv selector])); unsigned n = [[inv methodSignature] numberOfArguments]; unsigned i; for (i = 0; i < n-2; i++) { // first two arguments are the object and selector id arg; [inv getArgument:&arg atIndex:i+2]; NSLog(@"argument #%u: %@", i, arg); }
} @end
int main() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id example = [[Example alloc] init];
[example foo]; // prints "this is foo" [example bar]; // prints "this is bar" [example grill]; // prints "tried to handle unknown method grill" [example ding:@"dong"]; // prints "tried to handle unknown method ding:" // prints "argument #0: dong" [example release];
[pool release];
return 0;
}</lang>
OCaml
It is not possible in OCaml to call an undefined method, it will fail when trying to compile the code with an error message: <lang ocaml>Error: This expression has type example
It has no method ding</lang>
Perl
<lang perl>package Example; sub new {
bless {}
} sub foo {
print "this is foo\n";
} sub bar {
print "this is bar\n";
} sub AUTOLOAD {
my $name = $Example::AUTOLOAD; my ($self, @args) = @_; print "tried to handle unknown method $name\n"; if (@args) { print "it had arguments: @args\n"; }
} sub DESTROY {} # dummy method to prevent AUTOLOAD from
# being triggered when an Example is # destroyed
package main; my $example = Example->new;
$example->foo; # prints "this is foo" $example->bar; # prints "this is bar" $example->grill; # prints "tried to handle unknown method Example::grill" $example->ding("dong"); # prints "tried to handle unknown method Example::ding"
# and "it had arguments: dong"</lang>
PHP
<lang php><?php class Example {
function foo() { echo "this is foo\n"; } function bar() { echo "this is bar\n"; } function __call($name, $args) { echo "tried to handle unknown method $name\n"; if ($args) echo "it had arguments: ", implode(', ', $args), "\n"; }
}
$example = new Example();
$example->foo(); // prints "this is foo" $example->bar(); // prints "this is bar" $example->grill(); // prints "tried to handle unknown method grill" $example->ding("dong"); // prints "tried to handle unknown method ding"
// prints "it had arguments: dong
?></lang>
Python
Python objects can implement a __getattr__()
method to handle accesses of unknown attributes (methods are just attributes that are callable; so this function handles both methods and non-method fields). Here we assume that if you access an unknown attribute, you want a method, so we return a function that can be called.
<lang python>class Example:
def foo(self): print "this is foo" def bar(self): print "this is bar" def __getattr__(self, name): def method(*args): print "tried to handle unknown method " + name if args: print "it had arguments: " + str(args) return method
example = Example()
example.foo() # prints “this is foo” example.bar() # prints “this is bar” example.grill() # prints “tried to handle unknown method grill” example.ding("dong") # prints “tried to handle unknown method ding”
# prints “it had arguments: ('dong',)”</lang>
Ruby
<lang ruby>class Example
def foo puts "this is foo" end def bar puts "this is bar" end def method_missing(name, *args) puts "tried to handle unknown method %s" % name # name is a symbol unless args.empty? puts "it had arguments: %p" % [args] end end
end
example = Example.new
example.foo # prints “this is foo” example.bar # prints “this is bar” example.grill # prints “tried to handle unknown method grill” example.ding("dong") # prints “tried to handle unknown method ding”
# prints “it had arguments: ["dong"]”</lang>
Slate
Here is an example of unknown methods being used to call shell commands (this is already defined in the base image):
<lang slate>define: #shell &builder: [lobby newSubSpace].
_@shell didNotUnderstand: message at: position "Form a command string and execute it." [
position > 0 ifTrue: [resend] ifFalse: [([| :command |
message selector isUnarySelector ifTrue: [command ; message selector. message optionals pairsDo: [| :key :value | command ; ' -' ; (key as: String) allButFirst allButLast ; ' ' ; (value as: String)]]. message selector isKeywordSelector ifTrue: [| keywords args | keywords: ((message selector as: String) splitWith: $:). command ; keywords first. keywords size = 1 ifTrue: "Read a string or array of arguments." [args: message arguments second. (args is: String) ifTrue: [command ; ' ' ; args] ifFalse: [args do: [| :arg | command ; ' ' ; arg]]]]] writingAs: String) ifNil: [resend] ifNotNilDo: [| :cmd | [Platform run: cmd]]] ].</lang>
Here is an example of it being used:
<lang slate>slate[1]> shell ls: '*.image'. kernel.new.little.64.1244260494374694.image slate2.image net.image slate.image True slate[2]></lang>
Smalltalk
<lang smalltalk>Object subclass: CatchThemAll [
foo [ 'foo received' displayNl ] bar [ 'bar received' displayNl ] doesNotUnderstand: aMessage [ ('message "' , (aMessage selector asString) , '"') displayNl. (aMessage arguments) do: [ :a | 'argument: ' display. a printNl. ] ]
]
|a| a := CatchThemAll new. a foo. a bar. a weCanDoIt. a theyCanToo: 'eat' and: 'walk'.</lang>
Tcl
<lang tcl># First create a simple, conventional class and object oo::class create Example {
method foo {} { puts "this is foo" } method bar {} { puts "this is bar" }
} Example create example
- Modify the object to have a custom ‘unknown method’ interceptor
oo::objdefine example {
method unknown {name args} { puts "tried to handle unknown method \"$name\"" if {[llength $args]} { puts "it had arguments: $args" } }
}
- Show off what we can now do...
example foo; # prints “this is foo” example bar; # prints “this is bar” example grill; # prints “tried to handle unknown method "grill"” example ding dong; # prints “tried to handle unknown method "ding"”
# prints “it had arguments: dong”</lang>