Calling functions with variables

Say I had 3 functions; scene1(), scene2(), scene3(), and wanted to switch between these functions by changing an int variable ‘scene’. Is there any possible solution to this without the use of if/switch statements?

post your code in order to make the question useful for others and on other hand to explain your issue much more

Processing.GitHub.io/processing-javadocs/core/processing/core/PApplet.html#method-java.lang.String-

// https://Discourse.Processing.org/t/calling-functions-with-variables/30001/4
// GoToLoop (2021/May/09)

static final String SCENE = "scene";

void setup() {
  noLoop();
}

void draw() {
  background((color) random(#000000));
  final int chosenScene = (int) random(1, 4);
  method(SCENE + chosenScene);
}

void mousePressed() {
  redraw();
}

void scene1() {
  println(SCENE, 1);
}

void scene2() {
  println(SCENE, 2);
}

void scene3() {
  println(SCENE, 3);
}
5 Likes

Nice.

But is method() faster than switch(state){…?

This might be a somewhat more efficient and safer approach, although it probably doesn’t matter too much:

import java.lang.reflect.*;

Method[] scenes;

void setup() {
  scenes = getMethodsStartingWith("scene");
}

void draw() {
}

Method[] getMethodsStartingWith(String prefix) {
  try {
    ArrayList<Method> list = new ArrayList<Method>();
    Method[] all = getClass().getDeclaredMethods();

    for (Method method : all)
      if (method.getName().startsWith(prefix))
        list.add(method);

    return list.toArray(new Method[0]);
  } catch (SecurityException e) {
    e.printStackTrace();
  }

  return new Method[0];
}

void mousePressed() {
  try {
    scenes[int(random(scenes.length))].invoke(this);
  } catch (ReflectiveOperationException e) {
    e.printStackTrace();
  }
}

void scene1() {
  print(1);
}

void scene2() {
  print(2);
}

void scene3() {
  print(3);
}

Instead of having to find the method and thereby risk getting an exception and wasting time every mouse press, this code only searches through all the methods once and then stores the ones we need in an array.

The downside to this is of course that it’s not as clean as just doing method(...) and since it has to search through all methods in the beginning (which I suspect PApplet has a lot of) while method(...) probably just calls getMethod(name) behind the scenes, I’m not sure how often mousePressed has to be called for this version to be better than the other.

2 Likes

An object-oriented approach.
Not sure this would be more efficient as there would still be indirect access to the objects.

Handler[] scenes;

void settings()
{
  size(256,256);
  noLoop();
}

void setup()
{
  scenes = new Handler[2];
  scenes[0] = new Scene1();
  scenes[1] = new Scene2();
}

void mouseClicked()
{
  redraw();
}

void draw()
{
  background(0);
  int selected = (int) random(0,scenes.length);
  scenes[selected].show();
}

class Handler
{
  Handler() { }
  void show() { }
}

class Scene1 extends Handler
{
  void show() {
    noFill();
    stroke(0,0,255);
    rect(50,50, 150,50);
    fill(255);
    text("Showing Scene 1", 60,70);
  }
}

class Scene2 extends Handler
{
  void show() {
    noFill();
    stroke(255,0,0);
    rect(50,50, 150,50);
    fill(255);
    text("Showing Scene 2", 60,70);
  }
}

4 Likes

Hello,

Behind the scenes:
processing/PApplet.java at master · processing/processing · GitHub

import java.lang.reflect.*;

 /**
   * Call a method in the current class based on its name.
   * <p/>
   * Note that the function being called must be public. Inside the PDE,
   * 'public' is automatically added, but when used without the preprocessor,
   * (like from Eclipse) you'll have to do it yourself.
   */
  public void method(String name) {
    try {
      Method method = getClass().getMethod(name, new Class[] {});
      method.invoke(this, new Object[] { });

    } catch (IllegalArgumentException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.getTargetException().printStackTrace();
    } catch (NoSuchMethodException nsme) {
      System.err.println("There is no public " + name + "() method " +
                         "in the class " + getClass().getName());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

:)

1 Like

Interesting, thanks!

If we’re going to go into more detail, it appears that getMethod(name) isn’t O(1) after all and also just searches through an array of methods:

http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/Class.java

// The following code is simplified to only show the parts that matter

public Method getMethod(String name, Class<?>[] parameterTypes) {
  // searchMethods(...) is O(n)
  return searchMethods(privateGetDeclaredMethods(true), name, parameterTypes);
}

private static Method searchMethods(Method[] methods, String name, Class<?>[] parameterTypes) {
  Method res = null;
  String internedName = name.intern();
  
  // O(n) here:
  for (int i = 0; i < methods.length; i++) {
    Method m = methods[i];
    if (m.getName() == internedName
      && arrayContentsEq(parameterTypes, m.getParameterTypes())
      && (res == null
      || res.getReturnType().isAssignableFrom(m.getReturnType())))
      res = m;
  }

  return (res == null ? res : getReflectionFactory().copyMethod(res));
}


private Method[] privateGetDeclaredMethods(boolean publicOnly) {
  return // an array of methods stored in a field, so O(1)
}

Also, getClass().getDeclaredMethods() apparently just returns the methods added by the current class, not the ones of its superclass, so in case of the example I gave above it would be setup(), draw(), getMethodsStartingWith(String), mousePressed(), scene1(), scene2() and scene3(), which always makes that approach faster, I guess.

The object-oriented approach by @noahbuddy is still the one that makes the most sense though, but I figured it would be interesting to go further into this.

1 Like