Respond to an unknown method call: Difference between revisions
m (→{{header|C++}}: Remove wrong language name.) |
|||
Line 131: | Line 131: | ||
=={{header|Fancy}}== |
=={{header|Fancy}}== |
||
<lang fancy> |
<lang fancy> |
||
class CatchThemAll { |
|||
def foo { |
def foo { |
||
"foo received" println |
"foo received" println |
Revision as of 20:34, 3 October 2011
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.
See also Send an unknown method call.
Brat
<lang brat>example = object.new
example.no_method = { meth_name, *args |
p "#{meth_name} was called with these arguments: #{args}"
}
example.this_does_not_exist "at all" #Prints "this_does_not_exist was called with these arguments: [at all]"</lang>
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>
C++
Program terminates with diagnostic message, typically about a call to an "unimplemented pure virtual function". But in that situation, the function is actually known. The concept of a function not being known at all does not exist in C++, due to static name lookup checking. That is to say, the call a->b() causes the compiler to verify that the symbol b exists in the namespace associated with expression a, and refers to a function.
To avoid the pure virtual call, we can put some default function implementation into the abstract class which handles the situation (e.g. throws a custom exception or whatnot).
<lang cpp>class animal { public:
virtual void bark() // concrete virtual, not pure { throw "implement me: do not know how to bark"; }
};
class elephant : public animal // does not implement bark() { };
int main() {
elephant e; e.bark(); // throws exception
} </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 fancy> 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>
Fantom
In Fantom, you can call methods statically or dynamically. Static calls to methods will be checked at compile time. Dynamic method calls (indicated by an instance->method
syntax) are run through a "trap" method at run time. This method looks up the given method name, and throws an exception if the method/field is not known. This exception can be caught, and processed specially:
<lang fantom> class A {
public Void doit (Int n) { echo ("known function called on $n") }
// override the 'trap' method, which catches dynamic invocations of methods override Obj? trap(Str name, Obj?[]? args := null) { try { return super.trap(name, args) } catch (UnknownSlotErr err) { echo ("In trap, you called: " + name + " with args " + args.join(",")) return null } }
}
class Main {
public static Void main () { a := A() // note the dynamic dispatch a->doit (1) a->methodName (1, 2, 3) }
} </lang>
Output:
$ fan unknown-method.fan known function called on 1 In trap, you called: methodName with args 1,2,3
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
This is specifically the purpose of the __index metamethod:
<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, &block) 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>
Scala
As of scala 2.9, scalac must receive the -Xexperimental optional for Dynamic to receive this treatment. <lang scala>class DynamicTest extends Dynamic {
def foo()=println("this is foo") def bar()=println("this is bar") def applyDynamic(name: String)(args: Any*)={ println("tried to handle unknown method "+name) if(!args.isEmpty) println(" it had arguments: "+args.mkString(",")) }
}
object DynamicTest {
def main(args: Array[String]): Unit = { val d=new DynamicTest() d.foo() d.bar() d.grill() d.ding("dong") }
} </lang> Output:
this is foo this is bar tried to handle unknown method grill tried to handle unknown method ding it had arguments: dong
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>
- Programming Tasks
- Object oriented
- Brat
- C sharp
- C++
- Common Lisp
- D
- E
- Fancy
- Fantom
- J
- JavaScript
- Lua
- Objective-C
- Oz
- Perl
- PHP
- PicoLisp
- Python
- Ruby
- Scala
- Slate
- Smalltalk
- Tcl
- TclOO
- Ada/Omit
- Assembly/Omit
- AutoHotkey/Omit
- AWK/Omit
- C/Omit
- C++/Omit
- Go/Omit
- J/Omit
- Java/Omit
- Locomotive Basic/Omit
- M4/Omit
- OCaml/Omit
- PARI/GP/Omit
- PowerShell/Omit
- PureBasic/Omit
- Retro/Omit
- TI-89 BASIC/Omit
- UNIX Shell/Omit
- ZX Spectrum Basic/Omit