I have 2 methods, tappedNext() and tappedPrevious(), i need to call this function by passing a string variable, e.g onTap: tapped + part + (), is it possible to make a string interpolation for this like tapped${part}() ?
I certainly do not recommend you to do that. Instead, use a simple if statement.
But since you asked if it is possible..
Short answer, no (with a function only). Unless you're talking about methods, in that case we could use some mirrors.
You'll need to import the mirror package, it doesn't work on dartpad, so test it locally:
import 'dart:mirrors';
class MyClass {
tappedNext() {
print('Next Function');
}
tappedPrevious() {
print('Previous Function');
}
}
Now you must create an object from the class, create a mirror of it, and then make use of a Symbol to call the method in a reflectly way.
void main() {
final clazz = MyClass();
final mirror = reflect(clazz);
final function = 'Next';
Symbol s = Symbol('tapped$function');
mirror.invoke(s, []).reflectee;
}
That's it, the console will print:
Next Function
Related
So, I've created "interface class" with all static methods, which I want to expose to hscript scripts. It looks like this:
package com.application.interfaces.Terrain;
import com.application.TerrainCore
class Terrain {
private static var terrain:TerrainCore;
public static function _init(inTerrain:TerrainCore):Void {
terrain = inTerrain;
}
public static function test(s:Int):Void {
terrain.test(s);
}
}
The problem is, that I need to set terrain object somehow, but I don't want it to be exposed to scripts. I expose whole classes with
var interp = new Interp();
var module = Type.resolveClass("com.application.interfaces.Terrain");
interp.variables.set("Terrain", module)
The idea was to override method call in hscript.Interp so it doesn't execute any method named _init, but I don't know how to do that. Original call method looks like this:
function call( o : Dynamic, f : Dynamic, args : Array<Dynamic> ) : Dynamic {
return Reflect.callMethod(o,f,args);
}
Can you use a class instance of Terrain instead of using static members? Eg:
interp.variables.set("Terrain", new Terrain(new TerrainCore()));
Script users wont know if they are using static or instance methods as it will still be access via:
Terrain.test(123);
in script.
Another option (based on clemos), is to use rtti to work out what is allowed (instead of maintaining a list of it), eg:
Terrain._init(new TerrainCore());
_init is a private function now, so you need to #:allow it from your calling class (see below), also, you need to annotate with #:rtti so you can grab info about the functions at runtime, so Terrain now looks like:
#:rtti
class Terrain {
private static var terrain:TerrainCore;
#:allow(test.hscript.demo.Main)
private static function _init(inTerrain:TerrainCore):Void {
terrain = inTerrain;
}
public static function test(s:Int):Void {
terrain.test(s);
}
}
Finally, the script interp fcall now honours whether a field is public or private, ie:
public override function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic
var rtti = haxe.rtti.Rtti.getRtti(o);
for (field in rtti.statics) {
if (field.name == f && field.isPublic == false) {
error(EInvalidAccess(f));
}
}
return super.fcall(o, f, args);
}
Its worth noting that I used statics rather than fields for obvious reasons. Im also not sure what overhead this would cause with the loop and the rtti.
I believe it's fcall you should override, as call is used for toplevel calls only :
https://github.com/HaxeFoundation/hscript/blob/master/hscript/Interp.hx#L328-L331
It should be easy to filter f argument like :
if ( FORBIDDEN_FIELDS.indexOf( f ) > -1 ) throw EInvalidAccess( f );
or
if ( f.indexOf('_') == 0 ) throw EInvalidAccess( f );
I'm trying to use Nape with HaxeFlixel. Sadly, there's almost no documentation on how to use the addons.nape package and I just can't figure out why this code isn't moving the white rectangle (_test). (I left out imports for simplicity)
class PlayState extends FlxNapeState
{
var _test = new FlxNapeSprite(16, 16);
override public function create():Void
{
super.create();
_test.makeGraphic(16, 16);
_test.body.type = BodyType.KINEMATIC;
add(_test);
}
override public function update():Void
{
_test.body.velocity.x = 100;
super.update();
}
}
There are two issues with your code:
Directly initializing the _test variable leads to the FlxNapeSprite constructor call happening in the constructor of your PlayState. create() is called after the state constructor. This can cause crashes and otherwise weird behavior since Flixel does its internal cleanup between the constructor call of the new state and create() (graphics are disposed, for example, and in this case the Nape Space instance doesn't exist yet since it's created in the super.create() call).
The FlxNapeSprite constructor has a createRectangularBody argument which defaults to true and calls the function of that same name if true. Since you're not passing any asset to the constructor, it ends up creating a Shape with a width and height of 0. This leads to the following error:
Error: Cannot simulate with an invalid Polygon
Instead, you'll want to call createRectangularBody() manually after makeGraphic() to create a Shape that matches the graphic's dimensions.
The complete, working code looks like this:
package;
import flixel.addons.nape.FlxNapeSprite;
import flixel.addons.nape.FlxNapeState;
class PlayState extends FlxNapeState
{
override public function create():Void
{
super.create();
var _test = new FlxNapeSprite(16, 16);
_test.makeGraphic(16, 16);
_test.createRectangularBody();
_test.body.velocity.x = 100;
add(_test);
}
}
Regarding documentation, the FlxNape demo is a great resource to learn from.
If one pass a method as a funarg, how one can tell if passed function is a method, and get `this' object of a method is?
class A {
public function f():Void{
trace("f");
}
}
class B {
static function withFunarg(f:Void->Void):Void{
//HERE
}
public static function main(){
var a = new A();
withFunarg(a.f);
}
}
You cannot and there is no way to retrieve this. But it seems to me like an anti-pattern trying to do that. If you want the method and the container you can define a typedef:
typedef F = {
f : Void -> Void
}
Now you have the method and the container.
Haxe doesn't offer a cross-platform way to do that and it is generally not recomended.
But if you ultimately need this feature, you can use some platform-specific ways.
For example on js the following will work(at least on current haxe dev version):
static function getThis(f:Dynamic):Dynamic{
return (f.scope && f.method) ? f.scope : null;
}
It will return the object if the function is a method and a null otherwise. Result on calling on non-function is unspecified.
If you want to get the implicit `this' argument of a method, you have to make it explicit, like this
static function withMethodFunarg(o:{}, f:{}->Void):Void{
//HERE you have both object and function on this object
trace(o);
f(o);
}
public static function main(){
var a = new A();
withMethodFunarg(a,function(a){a.f()});
}
Which is, actually, pretty straight-forward: function is a function, no implicits, method caller is a method caller.
So I want to add methods to JDK classes like InputStream, File, etc. I'm trying to figure out what is the best way to do that, but it seems there are several options for doing it. One way is do this by adding methods into the metaClass property on the Class like so:
InputStream.metaClass.copy = { OutputStream out ->
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return delegate
}
Another way is using dynamic mixins like this:
class EnhancedInputStream {
static {
InputStream.metaClass.mixin( EnhancedInputStream )
}
public InputStream copy( OutputStream out ) {
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = mixinOwner.read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return mixinOwner
}
}
So the first question is do dynamic Mixins replace the use of using metaClass + Closure to create mixins? The examples of dynamic mixins don't really discuss scoping rules in any detail that I can find. Which leads me to the next point.
You can see in the first code sample using metaClass I used delegate to get access to the this pointer of the class I was adding methods to. What is the equivalent way to do that using dynamic Mixins? All examples I've found are stateless (pointless really). I found one example mentioning a special member mixinOwner that could be used in place of delegate. Is that true?
Second you'll see I used a static block in EnhancedInputStream to add the mixin dynamically to InputStream. When using metaClass what is the best way to add those? Another static block with import statement?
I suppose I really want just a compile time Mixin where I can define the #Mixin on the source of the mixin instead of destination because I didn't write the destination. Like
#MixinInto(File)
public class EnhancedFileMixin {
public void zip( File output ) {
// .....
}
}
But that doesn't appear to exist in Groovy land. So what's the best approach to reach this using metaClass or dynamic mixins?
I guess the nearest to #MixinInto would be the magic package convention. I couldn't mix it into a interface, but i managed to mix it into a FileInputStream, if that suits your case. I guess you can add state using the MetaClass which comes in the constructor.
To write a class to be mixed into InputStream. It needs to be:
In the package groovy.runtime.metaclass.java.io
Named FileInputStreamMetaClass (exactly)
Compiled and put into the classpath
Extend DelegatingMetaClass
It can only intercept the GroovyObject methods, so it is not so straightforward. If you are in for a pure dynamic groovy, it is great:
package groovy.runtime.metaclass.java.io
class FileInputStreamMetaClass extends DelegatingMetaClass {
FileInputStreamMetaClass(MetaClass meta) {
super(meta)
println "built FileInputStreamMetaClass"
}
Object invokeMethod(Object delegate, String method, Object[] args) {
switch (method) {
case "copy":
return "i'm copying stuff"
default:
return super.invokeMethod(delegate, method, args)
}
}
}
Compiling:
$ groovyc FileInputStreamMetaClass.groovy
$ groovy -cp . InputTest.groovy
A test:
InputStream input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "i'm copying stuff"
A bit cumbersome.
I'd go for Extensions any time of the day. Three files:
// file META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName=InputExtensionModule
moduleVersion=0.1
extensionClasses=InputStreamExtension
The extension:
class InputStreamExtension {
static String copy(InputStream input) {
"copying stuff, doc"
}
}
The test:
def input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "copying stuff, doc"
Compile and run:
$ groovyc InputStreamExtension.groovy
$ groovy ISExtensionTest.groovy
And i think the extension is the perfect place to use the static { mixin } block. With some changes:
class InputStreamExtension {
static {
InputStream.mixin InputStreamMixin
}
static String copy(InputStream input) { "copying stuff, doc" }
}
#Category(InputStream)
class InputStreamMixin {
Object pop() {
"input mixin pop"
}
}
A new test:
def input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "copying stuff, doc"
assert input.pop() == "input mixin pop"
Well I finally figured it out on my own. Essentially the this reference refers to the instance of the Mixin which doesn't do us much good. However, you can use the "as" keyword to convert that to the instance of the target class. For example:
class MyMixin {
static {
File mixin MyMixin
}
File foo() {
return this as File
}
}
File f = new File()
println( f.foo().equals( f ) )
As for mixinOwner and owner references that the jira bug refers to. They don't exist. This is the only way to get a reference to the instance that the mixin was added to.
I wrote up a longer blogpost about it because I thought this was important information for future Groovy programmers since there is zero official docs discussing this.
http://wrongnotes.blogspot.com/2013/06/groovy-mixins-and-this-pointer.html
I am glad you asked this question. To answer a very important question:
I suppose I really want just a compile time Mixin where I can define the #Mixin on the source of the mixin instead of destination because I didn't write the destination.
You cannot achieve this by #Mixin but we do have something in Groovy which will help you out. It is called #Category. Let me go through your example again to show you how you can actually effectively use this in category. Have a look at the below script:
#Category(InputStream)
class InputStreamCategory{
def copy(OutputStream out){
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = this.read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return this
}
}
class MyUtil{
def str = 'This is a dummy String!!!!'
InputStream inputS = new ByteArrayInputStream(str.bytes)
OutputStream outputS = new ByteArrayOutputStream()
def copy(){
use(InputStreamCategory){
inputS.copy(outputS)
println "Printing Output Stream: " + outputS
}
}
}
def util = new MyUtil()
util.copy()
//Prints:
Printing Output Stream: This is a dummy String!!!!
Explanation:-
#Category(InputStream) sets the this in InputStreamCategory and in your util class you just use the newly added method copy to InputStream. The benefit for using category is that you get hold of the caller object in this case inputS. The first parameter passed into a category always refers to the caller. You can have different categories for different implementations like FileCategory etc and then create an Utility Class to use those Categories. Therefore, you would end up with utilities like zip, copy, etc.
You could get detail information about the same from the api. I also highly recommend going through Category and Mixin Transformations.
To answer the first question:-
do dynamic Mixins replace the use of using metaClass + Closure to create mixins?
No they do not. metaClass implementation does not create a Mixin. It just adds another method in the metaData registry about the class while runtime. You get an handle to the delegate. On the other hand #Mixin gives you the ability to inherit pre-defined properties.
I would like to dynamically invoke a Class's Property via a String. In the following code, I can dynamically invoke a Class's Function via a String.
var myClass:Class = getDefinitionByName("myPackage.MyClass") as Class;
myClass["myStaticMethod"]();
where MyClass is defined as:
package myPackage {
public class MyClass {
public function MyClass() {}
public function myMethod():void {};
public static function myStaticMethod():void {};
public static function get myProperty():Object { return null; }
}
}
However, a Property, such as MyClass.myProperty is not a Function. So,
var myClass:Class = getDefinitionByName("myPackage.MyClass") as Class;
myClass["myProperty"]();
throws an error: TypeError: Error #1006: value is not a function because myProperty is not a Function.
Is there any way to do this dynamically via Strings?
Thanks for the help.
To solve this issue, I simply needed to remove the () from the code. That is, the new code looks like:
var myClass:Class = getDefinitionByName("myPackage.MyClass") as Class;
myClass["myProperty"]; // This works.
The Answer of Alex will indeed works properly, but only if you have the String written properly. Else you get this error thrown at you: TypeError: Error #1006: value is not a function. To avoid this you could try test if the property or method is defined before using it. Like so:
if(myClass["myProperty"] != undefined)
{
...
}
Anyhow, in your specific example you are requesting a getter, and that's why you had to remove the () from your source. If you would be needing a method, I would also recommend you to save the method as a function:
var myFunction: Function = myClass["theFunction"];
And then to use either the call or the apply methods.
myFunction.call(null, myParam);
IF you are interested in studying all the methods that an Object has and comparing them to a String. Consider also:
var child:Sprite = new Sprite();
var description:XML = describeType(child);
var methodList: XMLList = description.descendants('method');
The attributes of a <method/> node are:
name: The name of the method.
declaredBy: The class that contains the method definition.
returnType: The data type of the method's return value.
I hope this helps out, let me know if you found it useful.