Respond to an unknown method call: Difference between revisions
m →{{header|Tcl}}: corrected requirements |
|||
Line 161: | Line 161: | ||
example.ding("dong"); // alerts "tried to handle unknown method ding" |
example.ding("dong"); // alerts "tried to handle unknown method ding" |
||
// alerts "it had arguments: dong</lang> |
// alerts "it had arguments: dong</lang> |
||
=={{header|Lua}}== |
|||
<lang Lua> |
|||
local object={print=print} |
|||
setmetatable(object,{__index=function(t,k)return function() print("You called the method",k)end end}) |
|||
object.print("Hi") -->Hi |
|||
object.hello() -->You called the method hello |
|||
</lang> |
|||
=={{header|Objective-C}}== |
=={{header|Objective-C}}== |
Revision as of 19:21, 28 August 2010
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
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.
C#
<lang csharp>using System; using System.Dynamic;
class Example : DynamicObject {
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { result = null;
Console.WriteLine("This is {0}.", binder.Name); return true; }
}
class Program {
static void Main(string[] args) { dynamic ex = new Example();
ex.Foo(); ex.Bar(); }
}</lang>
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>
D
D v.2. code, from the Python version. D performs this statically. <lang d>import std.stdio: write, writeln;
struct Example {
void foo() { writeln("this is foo"); } void bar() { writeln("this is bar"); } void opDispatch(string name, Types...)(Types args) { write("tried to handle unknown method ", name); if (Types.length) { write(", it had arguments: "); foreach (arg; args) write(arg, " "); } writeln(); }
}
void main() {
Example example; example.foo(); // this is foo example.bar(); // this is bar example.grill(); // tried to handle unknown method grill example.ding("dong"); // tried to handle unknown method ding, it had arguments: dong
}</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>
Fancy
<lang ruby> def class CatchThemAll {
def foo { "foo received" println }
def bar { "bar received" println }
def unknown_message: msg with_params: params { "message: " ++ msg print; "arguments: " ++ (params join: ", ") println }
};
a = CatchThemAll new; a foo; a bar; a we_can_do_it; a they_can_too: "eat" and: "walk" </lang>
J
In J, you can define a method at invocation time, and this definition can be in z
which is the parent from which all objects and classes inherit.
For example, we could define
<lang J> example=:3 :0
doSomething_z_=: assert&0 bind 'doSomething was not implemented' doSomething__y
)
doSomething_adhoc1_=: smoutput bind 'hello world' dSomethingElse_adhoc2_=: smoutput bind 'hello world'</lang>
With those definitions in a fresh session (where adhoc2
has not been given a definition for doSomething
), we get the following behavior:
<lang J> example <'adhoc1' hello world
example<'adhoc2'
|doSomething was not implemented: assert </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>
Lua
<lang Lua> local object={print=print} setmetatable(object,{__index=function(t,k)return function() print("You called the method",k)end end}) object.print("Hi") -->Hi object.hello() -->You called the method hello </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>
Oz
To respond to unknown method calls, classes can implement the otherwise
method. As its sole argument, this method gets the received message, i.e. a record with the name of the unknown method as its label and the arguments as the record features.
<lang oz>declare
class Example meth init skip end meth foo {System.showInfo foo} end meth bar {System.showInfo bar} end meth otherwise(Msg) {System.showInfo "Unknown method "#{Label Msg}} if {Width Msg} > 0 then {System.printInfo "Arguments: "} {System.show {Record.toListInd Msg}} end end end Object = {New Example init} in {Object foo} {Object bar} {Object grill} {Object ding(dong)}</lang>
Output:
foo bar Unknown method grill Unknown method ding Arguments: [1#dong]
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>
PicoLisp
The function 'try' is used to send a message to an object for which it is not known whether it inherits a method for that message or not. As opposed to the syntacically equivalent 'send' function, 'try' does not give an error, but returns NIL. <lang PicoLisp>(unless (try 'unknown> Obj)
(anotherMethod> Obj) )</lang>
<lang PicoLisp>(or
(try 'message1> Obj) (try 'message2> Obj) (try 'message3> Obj) (send *FinalMessage Obj) )</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
or
<lang tcl>package require TclOO
- 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>