Dynamic class loading

Here's a super simplistic example. Say I have a class that draws shapes, and I want to be able to specify the shape to draw as one of the parameters.

<layout id="WatchFace">
<drawable id="myShape" class="ShapeDrawer">
<param name="color">Graphics.COLOR_ORANGE</param>
<param name="shape">"Circle"</param>
</drawable>
</layout>

For example the code might look like this:

class Circle {
}

class ShapeDrawer extends Ui.Drawable {
	protected var shape;
	
	function initialize(params) {
        Drawable.initialize(params);
        
        var shapeName = params.get(:shape);
        
        shape = new shapeName(); // Obviously this won't work
    }
}

Is it possible in Monkey C to instantiate a class dynamically? I haven't been able to find any documentation that seems to support loading a class by name. 

My next thought was perhaps there was support to load a class through a resource file, since classes like Drawable, Bitmap, and Font can be instantiated. But I'm not sure if it is possible to load a user defined class that way.

If neither of these are possible, are there any other mechanisms available to load a class?

  • Possibly off-topic, but... 

    Memory is very limited and my experience is that what makes code readable and nice (like classes for everything, following a pattern / interface) might severely curtail the devices it can run on due to memory limitations. 

    Stone-age, perhaps, but parameterised display could easily be more efficient. As an example, I had a single method overwritten in a subclass and the DF wouldn’t run on half my target devices. I replaced it with a conditional and saved a good chunk so that it would... and then gained a chunk more by engineering the class out of existence... then gained a chunk more by inlining some stuff I’d had as methods. 

    Short answer: you probably cannot do what you want, but that may be a good thing. 

  • It doesn't come up as much these days, but back when there was a 16kb limit on data fields, one way to reduce memory was not not use layouts and just do your own direct dc calls.  The reason is the layout actually generates some code when it's compiled by the resource compiler.

    There's no way to dynamically load a class.  The code for the class is in the .prg, even if you don't use it. .

  • Something like a barrel is interesting, as some may think is a compiled thing that just gets added/loaded when needed, but it's not.

    A .barrel file is actually just a .zip of a barrel project, and when the main project uses it, the barrel gets extracted/rebuilt when you build the main project and becomes part of the code.

  • Thanks, that's probably good advice. I haven't done anything complicated enough to run into memory limits yet. 

    Right now I have some external code generating a Monkey C file. I was trying to put in a layer of abstraction that could use that generated file. But it sounds like that may be a liability.  I can probably just generate everything into a single Drawable and use that, even if it limits the flexibility of how I use my class.

    Thanks for the feedback,

  • There is some kind of class loading going on, but it is unlikely exposed for general use. In the XML config for a <drawable> you can specify a class attribute, so that class is getting loaded somehow. 

    But like  mentioned, all the class overhead is probably not worth trying to write the code this way. I'll probably just inline everything instead of trying to use more classes. 

    Thanks for the feedback!

  • There is one situation where it can be useful. The compiled code also takes some memory, and if you are trying to optimize the every last bit, you may want this.

    I had a couple of similar objects (collection, guide, tour...) with the same methods, and I wanted to create them dynamically. But probably the if/else block was enough.

    We have the symbols (:Name), we have the reference to a method, and even the symbol (as an enumeration) can be passed as the method name Slight smile This can be used to generate the UI dynamically... But for most apps the more simple and straightforward approaches are enough.

  • Former Member
    Former Member over 5 years ago

    You can use shapeName further in switch:

    switch ( shapeName ) {
        case "className": {
            var obj = new className();
            break;
        }
    }