Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert global enum values to string in Godot?

Tags:

enums

godot

The "GlobalScope" class defines many fundamental enums like the Error enum.

I'm trying to produce meaningful logs when an error occurs. However printing a value of type Error only prints the integer, which is not very helpful.

The Godot documentation on enums indicates that looking up the value should work in a dictionary like fashion. However, trying to access Error[error_value] errors with:

The identifier "Error" isn't declared in the current scope.

How can I convert such enum values to string?

like image 999
bluenote10 Avatar asked May 24 '20 10:05

bluenote10


2 Answers

In the documentation you referenced, it explains that enums basically just create a bunch of constants:

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

However, the names of the identifiers of these constants only exist to make it easier for humans to read the code. They are replaced on runtime with something the machine can use, and are inaccessible later. If I want to print an identifier's name, I have to do so manually:

# Manually print TILE_FLOOR's name as a string, then its value.
print("The value of TILE_FLOOR is ", TILE_FLOOR)

So if your goal is to have descriptive error output, you should do so in a similar way, perhaps like so:

if unexpected_bug_found:
    # Manually print the error description, then actually return the value.
    print("ERR_BUG: There was a unexpected bug!")
    return ERR_BUG

Now the relationship with dictionaries is that dictionaries can be made to act like enumerations, not the other way around. Enumerations are limited to be a list of identifiers with integer assignments, which dictionaries can do too. But they can also do other cool things, like have identifiers that are strings, which I believe you may have been thinking of:

const MyDict = {
    NORMAL_KEY = 0,
    'STRING_KEY' : 1, # uses a colon instead of equals sign
}
func _ready():
    print("MyDict.NORMAL_KEY is ",    MyDict.NORMAL_KEY)    # valid
    print("MyDict.STRING_KEY is ",    MyDict.STRING_KEY)    # valid
    print("MyDict[NORMAL_KEY] is ",   MyDict[NORMAL_KEY])   # INVALID
    print("MyDict['STRING_KEY'] is ", MyDict['STRING_KEY']) # valid
    # Dictionary['KEY'] only works if the key is a string.

This is useful in its own way, but even in this scenario, we assume to already have the string matching the identifier name explicitly in hand, meaning we may as well print that string manually as in the first example.

like image 187
Starkle Avatar answered Oct 22 '22 16:10

Starkle


The naive approach I done for me, in a Singleton (in fact in a file that contain a lot of static funcs, referenced by a class_name)

static func get_error(global_error_constant:int) -> String:
    var info := Engine.get_version_info()
    var version := "%s.%s" % [info.major, info.minor]
    var default := ["OK","FAILED","ERR_UNAVAILABLE","ERR_UNCONFIGURED","ERR_UNAUTHORIZED","ERR_PARAMETER_RANGE_ERROR","ERR_OUT_OF_MEMORY","ERR_FILE_NOT_FOUND","ERR_FILE_BAD_DRIVE","ERR_FILE_BAD_PATH","ERR_FILE_NO_PERMISSION","ERR_FILE_ALREADY_IN_USE","ERR_FILE_CANT_OPEN","ERR_FILE_CANT_WRITE","ERR_FILE_CANT_READ","ERR_FILE_UNRECOGNIZED","ERR_FILE_CORRUPT","ERR_FILE_MISSING_DEPENDENCIES","ERR_FILE_EOF","ERR_CANT_OPEN","ERR_CANT_CREATE","ERR_QUERY_FAILED","ERR_ALREADY_IN_USE","ERR_LOCKED","ERR_TIMEOUT","ERR_CANT_CONNECT","ERR_CANT_RESOLVE","ERR_CONNECTION_ERROR","ERR_CANT_ACQUIRE_RESOURCE","ERR_CANT_FORK","ERR_INVALID_DATA","ERR_INVALID_PARAMETER","ERR_ALREADY_EXISTS","ERR_DOES_NOT_EXIST","ERR_DATABASE_CANT_READ","ERR_DATABASE_CANT_WRITE","ERR_COMPILATION_FAILED","ERR_METHOD_NOT_FOUND","ERR_LINK_FAILED","ERR_SCRIPT_FAILED","ERR_CYCLIC_LINK","ERR_INVALID_DECLARATION","ERR_DUPLICATE_SYMBOL","ERR_PARSE_ERROR","ERR_BUSY","ERR_SKIP","ERR_HELP","ERR_BUG","ERR_PRINTER_ON_FIR"]
    match version:
        "3.4":
            return default[global_error_constant]
    # Regexp to use on @GlobalScope documentation
    # \s+=\s+.+  replace by nothing
    # (\w+)\s+ replace by "$1", (with quotes and comma)
    printerr("you must check and add %s version in get_error()" % version)
    return default[global_error_constant]

So print(MyClass.get_error(err)), or assert(!err, MyClass.get_error(err)) is handy

For non globals I made this, though it was not your question, it is highly related.

It would be useful to be able to access to @GlobalScope and @GDScript, maybe due a memory cost ?

static func get_enum_flags(_class:String, _enum:String, flags:int) -> PoolStringArray:
    var ret := PoolStringArray()
    var enum_flags := ClassDB.class_get_enum_constants(_class, _enum)
    for i in enum_flags.size():
        if (1 << i) & flags:
            ret.append(enum_flags[i])
    return ret


static func get_constant_or_enum(_class:String, number:int, _enum:="") -> String:
    if _enum:
        return ClassDB.class_get_enum_constants(_class, _enum)[number]
    return ClassDB.class_get_integer_constant_list(_class)[number]
like image 1
Vincent Avatar answered Oct 22 '22 16:10

Vincent