Godot: Allow scripts marked with `class_name` to use their class within the script without causing a cyclic dependency

Created on 23 Jan 2019  路  10Comments  路  Source: godotengine/godot

Godot version:
3.1 beta 2

OS/device including version:
Windows

Issue description:
As suggested in the title, having a script not be able to use its own Class name in its own script is severely limiting. For instance, one could not make any factory methods or methods that make more of the class. I came across this limitation when trying to make a 'Set' class. Trying to use the identifier Set results in an error: Parser Error: Using own name in class file is not allowed (creates a cyclic reference)

extends Reference
class_name Set

# with static typing
# this line causes an error
func union(other: Set) -> Set: 
    # this line also causes an error
    var result = Set.new() 
    # do stuff
    return result

# even without static typing

func intersection(other):
    # we can't even do this
    var result = Set.new()
    # do stuff
    return result

func copy():
    return Set.new(my_items)
archived gdscript usability

Most helpful comment

Temporary Workaround
I found a way to do what I want here, but it feels inelegant and renaming the script or moving it becomes a hassle.

extends Reference
class_name Set

var Self = load("res://set.gd") # set.gd is the name of this very script

func union(other): 
    var result = Self.new() 
    # do stuff
    return result

func intersection(other):
    var result = Self.new()
    # do stuff
    return result

func copy():
    return Self.new(my_items)

but this still disallows me from using static typing since I can't then use Self as a valid type

All 10 comments

Temporary Workaround
I found a way to do what I want here, but it feels inelegant and renaming the script or moving it becomes a hassle.

extends Reference
class_name Set

var Self = load("res://set.gd") # set.gd is the name of this very script

func union(other): 
    var result = Self.new() 
    # do stuff
    return result

func intersection(other):
    var result = Self.new()
    # do stuff
    return result

func copy():
    return Self.new(my_items)

but this still disallows me from using static typing since I can't then use Self as a valid type

Seems like a duplicate of #21461..

UPDATE: It seems as of 3.1 beta 3, this is now allowed

class_name MyClass

func test(instance: MyClass) -> MyClass:
    return instance

Thank you to whoever worked on this issue! It really helps a lot.

However I cannot close this yet as the following is still not allowed

class_name MyClass

var id: int

func _init(n: int):
    id = n

func clone() -> MyClass:
    return MyClass.new(id) # still causes the cyclic dependency error

Mmm, I ran into this issue too. Need to check if a nodes parent is the same class as it, but can't. Be nice to see it patched out soon. :/

I found a work around by overriding get_class to give the right answer, then going "get_class().case_comp(parent.get_class()), but it's kind of a hack. Being able to do things properly would be nice. :/

While you can use the class_name as type hints to parameters of methods of your class, you cannot use them for example in factory methods which will create new instances of the class. The latter will give you the error described by the OP. E.g.

extends Node
class_name Useful

func equals(p_other: Useful) -> bool:

  return false

func copy() -> Useful:

  return Useful.new()

will result in the describe error condition, and while equals will work just fine, copy will result in the parser complaining about a cyclic reference.

To resolve this issue, one must use get_script().new(), e.g.

func copy() -> Useful:

  return get_script().new(with-params)

and if you have a base class that implements this, you need to

func copy() -> TimeInfo:

  return get_script().get_base_script().new(with-params)

This looks rather complicated, but it is a valid work around and also removes the need for the extraneous preload.

You can't use class_name type hints even in parameters if some other class also has a type-hinted reference to your class.

Having a main scene with

extends Node2D

func _ready():
    var attribute : Attribute = null

And a separate class with:

extends Node
class_name Attribute

func function(other_attribute : Attribute) -> void:
    pass

will still fail in eb98c5e04 with a cyclic dependency error, though removing either one of the Attribute hints will succeed.

Also in my particular project the cyclic dependency error is not shown by the editor so this manifests as a resource leak.

I hope my finding will help at least some of you.

Short: I could check own custom class when I used as operator.
Long:
I'm developing a mission objectives system for my game and use a hierarchy of nodes of custom class MissionObjectiveBase which have child nodes inheriting from this custom class.

I got the cyclic dependency error when I used the is operator:
child is MissionObjectiveBase

but it worked fine when I used the as operator:
child as MissionObjectiveBase

I use Godot 3.2.

@RomanKoziolek nice find! I've also tested and confirmed this to be working in 3.2!

It's just a pity that this kind of obscure hack is needed in the first place, so I hope this is properly fixed in GDScript soon...

This is a duplicate of #21461.

Was this page helpful?
0 / 5 - 0 ratings