Open Sirosky opened 4 years ago
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.
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:).
Trying something similar, I did:
func print_name_and_value_of_variable(string):
var script_var_array = get_script().get_script_property_list()
for i in script_var_array:
if i["name"] == string:
print(i["name"] + " is " + str(get(string)))
break
And I can call that inside the script and just pass in a string of my variable:
print_name_and_value_of_variable("weight")
which yields a simple:
or:
I think this would work for the OP just by extending it to print/display where they want?
@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...
OFFTOPIC: why is it called printerr() without underscore character? I recommend to align it and make it print_err() like in print_debug() (!)
OFFTOPIC: why is it called printerr() without underscore character? I recommend to align it and make it print_err() like in print_debug() (!)
The conventions to refer to standard output and standard error are stdout
and stderr
respectively.
Also, to print an error message, use push_error()
instead of printerr()
as it performs automatic coloring in the console.
I'd also like something similar as I want to override properties without modifying properties and I currently have methods take a dictionary argument with key being property name but I had to use enums for type safety.
Although I prefer an operator like &property
or `property`
and also available for Class.property
, i.e. &Class.property
or `Class.property`
An argument for a CSharp-like nameof function: let's assume I want to do an assertion, to prove something is not null:
assert(my_control != null, "my_control != null")
is how I would do that currently. However, I would much more prefer to do assert(my_control != null, nameof(my_control)+"!= null")
, such that I get an error should I forget to update the name of the my_control
variable, or such that it automatically updates when using refactor tools (as suggested in #899 ).
This functionality is however different to @vnen's suggestion, that calling func my_fun(variable): print(nameof(variable))
should print the variable name in the caller, whereas for my use case, it should just print "variable", as it is named in the callee. The two functionalities are different, and a CSharp-like nameof would be much easier to implement, and would greatly improve refactorability of GDScript (main reason for many people to prefer CSharp).
This functionality is however different to @vnen's suggestion
It was never a suggestion, I was just showing that this is way less useful without macros.
I was just showing that this is way less useful without macros.
which is precisely what I disagree with. Using "my_var"
instead of nameof(my_var)
might seem shorter, but it is highly error-prone, since misspelling or forgetting to rename when refactoring will go undetected. The past has shown, that making GDScript rely on strings so much is a bad idea, so string usage (e.g. connect("my_sig", self, "my_fun")
) has been reduced a lot from Godot 3 to Godot 4. Implementing nameof()
would be the next step. I actually think it should have been included in 4.0 already.
I agree with the suggestion above that we should use an operator or special syntax in the language. I'd also like to suggest that it could have an optional prefix for scoping, that isn't part of the string.
IMO the best syntax would be with delimiters (but still color the inner part as code, not as string):
`x`
| SomeClass.etc.`some.prop`
(resolves to a string if code would be valid)
&`x`
| SomeClass.etc.&`some.prop`
(StringName version)
Instead of emitting the evaluation of that code, you emit the code itself as a String|StringName, but still check it.
x
isn't in scope, then raise a compile-time error; otherwise it resolves to 'x'
or &'x'
.SomeClass.etc
can't be statically determined to have .some.prop
, then raise a compile-time error. Otherwise, it resolves to 'some.prop'
(or &'some.prop'
) only, dropping the scoping prefix.Delimiters are better as they allow for a possible improvement where you could stringify any valid code. For example:
MyMath.`sqrt(a.position.x ** 2 + a.position.y ** 2)`
(error if it's an invalid expression grammatically or any identifier is not in scope; no evaluation is performed)
This feature is very necessary, as there's still many APIs relying on reflection/StringName. Theoretical example:
Array(a, TYPE_OBJECT, &`Spatial`, null)
-> error in Godot 4, Spatial
is not an identifier anymore
Array(a, TYPE_OBJECT, &`Node3D`, null)
-> works after fixing
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.