Godot-proposals: Add function to stringify GDScript symbols

Created on 14 May 2020  路  11Comments  路  Source: godotengine/godot-proposals

Describe the project you are working on:
I am creating a tooltip for debugging purposes.

Describe the problem or limitation you are having in your project:
There is no way to stringify GDScript symbols. For example, I would like the tooltip to display all of muh_array = [1,2,3]. It's easy enough to display the contents of the array itself, but _not_ the name of the array. You have to do it manually.

Describe the feature / enhancement and how it helps to overcome the problem or limitation:
Add some global function to use reflection and fetch the name of a variable. It would eliminate the need to pass on the name as a string manually.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
Using muh_array is an example:

print(muh_array.get_name())

If this enhancement will not be used often, can it be worked around with a few lines of script?:
Yes, just one extra line haha.

Is there a reason why this should be core and not an add-on in the asset library?:
This touches upon the core of how GDScript works, and could be a very useful debugging option.

gdscript

Most helpful comment

Well, certainly, the workaround is "just one extra line", but this is presumably something you would be using often, across multiple projects, to create more detailed reporting (whether it be tooltips, logging, or app notifications of some sort). In addition to that, what cannot be reproduced with script code is a way of generically fetching the name, since the workaround is to just hardcode the string value which can't be done programmatically at runtime.

I don't think the syntax for it would be with a method, as you indicated, because that would imply that the Godot API is the thing that recognizes and returns the GDScript symbol name (which only the GDScript object itself could know, not the engine code it rests upon).

It could be a special global function in the @GDScript namespace. The soon-to-be-added annotations aren't a good place for it, as those are declarative and don't go into expressions. GDScript has no C-like macro system, and adding one just for the purpose of this would be overkill. So, yeah, I think an @GDScript function would be the best place for something like this.

Edit: the implementation could just be that this particular function is something that only the GDScriptParser sees, and it simply replaces it with the string of the identifier the function wraps. The compiler wouldn't even see it.

@vnen Thoughts? Since you're the one currently doing the rewrite?

All 11 comments

Well, certainly, the workaround is "just one extra line", but this is presumably something you would be using often, across multiple projects, to create more detailed reporting (whether it be tooltips, logging, or app notifications of some sort). In addition to that, what cannot be reproduced with script code is a way of generically fetching the name, since the workaround is to just hardcode the string value which can't be done programmatically at runtime.

I don't think the syntax for it would be with a method, as you indicated, because that would imply that the Godot API is the thing that recognizes and returns the GDScript symbol name (which only the GDScript object itself could know, not the engine code it rests upon).

It could be a special global function in the @GDScript namespace. The soon-to-be-added annotations aren't a good place for it, as those are declarative and don't go into expressions. GDScript has no C-like macro system, and adding one just for the purpose of this would be overkill. So, yeah, I think an @GDScript function would be the best place for something like this.

Edit: the implementation could just be that this particular function is something that only the GDScriptParser sees, and it simply replaces it with the string of the identifier the function wraps. The compiler wouldn't even see it.

@vnen Thoughts? Since you're the one currently doing the rewrite?

This could also be helpful for #538.

Well, we could add some magic functions for this. Not really reflection, but more like macro substitution. I've thought of adding __FILE__ and __FUNCTION__ but those are not super useful without actual macros (which I don't plan to implement).

So for this we could use: __nameof(my_var) which just spits the string "my_var" in the code. But then, it's much simpler to just use "my_var" directly.

Which is what @willnationsdev added:

Edit: the implementation could just be that this particular function is something that only the GDScriptParser sees, and it simply replaces it with the string of the identifier the function wraps. The compiler wouldn't even see it.

It's not useful without macros because if you want to do it in a function:

func print_var(variable):
    print("%s = %s" % [__nameof(variable), str(variable)])

It won't work as you expected:

var x = 42
var y = 23
print_var(x)
print_var(y)

You want this to print:

x = 42
y = 23

but it will actually print:

variable = 42
variable = 23

So in the end if you have to something like in the original post: print(muh_array.get_name()) it's easier to just do print("muh_array").

Ahh, I see. Yeah, it isn't really possible without having actual macros or reflection. And I don't think there are any plans to add that sort of complexity to GDScript.

This could also be helpful for #538.

Yes, I also think that we need to separate print() and dump() functionality. And dump() can work like a macro. For example:

func _ready():
    # ...
    dump(a, b)
    dump("My error message", my_array, my_string)
    # ...

Will print:

res://script.gd:23 - _ready()
-----------------------------
a == 1
b == 2

res://script.gd:24 - _ready()
-----------------------------
My error message
my_array == [1, 2, 3]
my_string == "Hello!"

@dalexeev I would be much more inclined to suggest to change, printerr() to print out to the console with some more information like script line and function details.

It's kinda annoying that it errs with just the string and doesn't even log to the debugger IIRC...

@swarnimarun You are supposed to use push_error() to print text to both the console and the in-editor debugger. There is also push_warning() to match it for warnings instead of errors. Not sure if it highlights the script line and function details though...it definitely should if it doesn't.

Not sure if it highlights the script line and function details though...it definitely should if it doesn't.

That's already implemented :slightly_smiling_face:

@willnationsdev @Calinou yep it works.

Though while testing I noticed that when executed as a tool script. logs refer to the c++ source only not the gdscript source/stack trace which should be doable.
image

Same for push_warning.

In works perfectly at the time of the active game.

Also while trying to create an issue for above I noticed this issue,
https://github.com/godotengine/godot/issues/28458

This proposes a nice idea that we can just have expression eval and return the value while debug printing it in the editor.

var val = dbg(op(a) + op(b) * 2.0)
# op(a) + op(b) * 2.0 = <someval>

Though it can just as well already be done by,

var val = dbg("op(a) + op(b) * 2.0", op(a) + op(b) * 2.0)
# op(a) + op(b) * 2.0 = <someval>

But dbg would increase the convenience of debugging a bunch.
And should be achievable as per what @vnen commented earlier.

actual macros (which I don't plan to implement)

:cry:

So for this we could use: __nameof(my_var) which just spits the string "my_var" in the code

This looks exactly like nameof from C#:

var numbers = new List<int> { 1, 2, 3 };
Console.WriteLine(nameof(numbers));  // output: numbers

I personally don't really like the __ prefix, it seems to me like "really internal, don't even think about touching it" (possibly a result of being exposed to too much JS :smile:).

Was this page helpful?
0 / 5 - 0 ratings