Run failed with class import "Invalid symbol used in the context of a function invocation. No function exists with the given symbol name."

Former Member
Former Member

Hello everyone, 

I have faced with problem of my class importing:

I had following class in source:

using Toybox.Application;

class AppProperty{
	
	function initialize(){
	}
	
	function getValue(value){
	if(Application has :Storage) {return Application.Properties.getValue(value); }
	else { return Application.getApp().getProperty(value); }
	}
}

Another class BackgroundDrawable.mc in source/drawable has instance of class AppProperty:

using Toybox.Graphics;
using Toybox.WatchUi as Ui;
using Toybox.System;
using AppProperty;

class BackgroundDrawable extends Ui.Drawable
{
	
	private var clockBackgroudColor = AppProperty().getValue("ClockBacgroundColor");
	private var avtivityBackgroudColor = AppProperty().getValue("ActivityBackgroundColor");
	....
}

When I run project following error occurred:

Invalid symbol "AppProperty" used in the context of a function invocation. No function exists with the given symbol name.

Thanks

  • I think Travis will still tell me off for bad practice as you are using a class method without a class instance, but...

    // THIS ONE SHOULD WORK
    AppProperty.getValue(key); // Read from the class definition and execute getValue
    
    // THIS ONE SHOULD GIVE AN ERROR
    AppProperty().getValue(key); // Try to execute getValue() on the result of executing AppProperty();
    
    // THIS ONE SHOULD WORK, TOO
    var f = new AppProperty();
    f.getValue(key); // Read from the instance method getValue
     

    So I think you meant the first one ("AppProperty.getValue()").

    But if so, you don't really need the overhead of a class. Why not just make that a module method?

  • Former Member
    0 Former Member over 4 years ago in reply to 9635560

    Right! I missed "new"! My bad.

    Anyway is it fine import class like:

    using AppProperty;

    Or is it useless?

  • That's an ecumenical matter... Slight smile

    Does it work? Yes. 

    Is it bad practice? No.

    Is it necessary? No.

    Is it a good idea? That's moot...

    MonkeyC has all kinds of ways to influence memory usage. 

    There is a trade-off between readable, usable, maintainable code and the ability to run that code on older devices with less memory.

    Creating lots of classes looks nice and is very readable and easy to manage... BUT ... Every class definition you create consumes memory, every instance of a class consumes memory, even the dot notation to reference the class methods consumes memory... BUT ... it also adds load to the processor to locate the method reference if it isn't somewhere obvious.

    So...

    While you could do as you are doing and use a separate class to hold the getValue method (which is essentially static and refers to absolutely zero class specific entities or options...) you could just as easily place it:

    1. On the View class (limiting access to the view unless you use view.getValue)

    2. On the app class (which will be recursed to if not found on the view)

    3. Globally, outside of any class (which will be recursed to if not found on the app or view)

    All three of those would work and you could then refer to "getValue(key)" rather than "AppProperty.getValue(key)"... at a slight hit to processor, but with a net benefit in terms of compiled code weight and runtime memory consumption.

    Use Ctrl+M in the simulator to view memory changes as you make code changes. It's enlightening.

  • If you are worried about battery performace, you should rethink how you are doing this.

    With settings, there are only two places you need to use getProperty/getValue - when the app starts or when onSettingsChanged is called and then just save the vale.  The way you are doing it, they are called a couple times every second in when drawing.

    The same for the "Application has :Storage".  You can set a boolean for that when the app starts, as it will never change, and checking a boolean is cheaper than checking a dictionary for a symbol (that's what "has" is doing)

  • I'm not going to tell you off for bad practices, but I'll definitely explain what you should do so you can make the code you suggest correct. As it stands now it just so happens to work because of a fluke in our virtual machine. If we ever fix that, it will explode.

    If you want to call AppProperty.getValue() using that syntax, you should either make AppProperty a module (instead of a class) or you should declare the getValue function to be static...

    module AppProperty {
    
      function getValue(key) {
          // implementation
      }
      
    }

    or

    class AppProperty {
    
      static function getValue(key) {
          // implementation
      }
      
    }

    It is not legal to call a function on an object type unless that function is static (all module functions are implicitly static).

    The reason that the OP's code didn't work is that he wasn't creating a temporary AppProperty object like he thought he was (it looks like his background may be C++). In MonkeyC if you want to create an instance of a user-defined object type, you have to use a new expression (new AppProperty()). This gives you an object instance which you can then use to call a member function on.

  • Former Member
    0 Former Member over 4 years ago in reply to Travis.ConnectIQ

    Thanks a lot, guys! That is very useful information for me. I am not used syntex yet, but common idea is quite clear.