Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for fully static classes #3955

Closed
Shadowblitz16 opened this issue Feb 12, 2022 · 10 comments
Closed

Add support for fully static classes #3955

Shadowblitz16 opened this issue Feb 12, 2022 · 10 comments

Comments

@Shadowblitz16
Copy link

Shadowblitz16 commented Feb 12, 2022

Describe the project you are working on

A yieldable signal dispatcher

Describe the problem or limitation you are having in your project

Right now I am using a autoload but Nodes have members that clash with the names I want to give it for consistency

extends Node

var count := { }

class SignalCount:
	var count := 0
	signal complete
	
	func register(_source:Object, _signal:String)->void:
		var err =  _source.connect(_signal, self, "decrement", [], CONNECT_ONESHOT)
		if  err == OK:
			count += 1
		else:
			print(err)
			
	func decrement():
		count -= 1
		if count == 0:
			emit_signal("complete")

func connect(_source:Object, _signal:String, _target:Object, _method:String, _binds:=[], _flags:=0)->int:
	return _source.connect(_signal, _target, _method, _bind, _flags)
	
func disconnect(_source:Object, _signal:String, _target:Object, _method:String):
	return _source.disconnect(_source, _target, _method)
	
func emit_signal(_source:Object, _signal:String, _args:Array):
	var countedSignals = SignalCount.new()
	emit_signal("post_pressed")
	
	for connection in _node.get_signal_connection_list(_signal):
		countedSignals.register(_node, connection["signal"])
		
	yield(countedSignals, "complete")
	

usually the answer is to use a static class but gd script doesn't support those.
You can't overwrite the functions either.

static class Signal:

      var count := { }
      
      class SignalCount:
	      var count := 0
	      signal complete
	      
	      func register(_source:Object, _signal:String)->void:
		      var err =  _source.connect(_signal, self, "decrement", [], CONNECT_ONESHOT)
		      if  err == OK:
			      count += 1
		      else:
			      print(err)
			      
	      func decrement():
		      count -= 1
		      if count == 0:
			      emit_signal("complete")
      
      func connect(_source:Object, _signal:String, _target:Object, _method:String, _binds:=[], _flags:=0)->int:
	      return _source.connect(_signal, _target, _method, _bind, _flags)
	      
      func disconnect(_source:Object, _signal:String, _target:Object, _method:String):
	      return _source.disconnect(_source, _target, _method)
	      
      func emit_signal(_source:Object, _signal:String, _args:Array):
	      var countedSignals = SignalCount.new()
	      emit_signal("post_pressed")
	      
	      for connection in _node.get_signal_connection_list(_signal):
		      countedSignals.register(_node, connection["signal"])
		      
	      yield(countedSignals, "complete")
	      

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Please either support static classes and variables along side static functions,
or allow us to autoload a base node type that has no members at all.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

possibly like above or like this...

extends Empty

If this enhancement will not be used often, can it be worked around with a few lines of script?

It would be most likely be used for utility classes, It cannot be worked around

Is there a reason why this should be core and not an add-on in the asset library?

This would be use in cases you want to reduce size of executable and have naming consistency

@dalexeev
Copy link
Member

See the comment.

@YuriSizov
Copy link
Contributor

You can have static classes, just not static properties. Your static class can extend Reference or even Object, though in practice those are about the same memory-wise (considering you don't need an instance anyway).

Static properties can be hacked with a constant that is assigned a dictionary, since dictionary can still be modified. For example:

class_name TextUtils
extends Reference

# It's a trick to have a static instance of RegEx.
# Bad bad hacks, but it does the job and static class > autoload helper.
const _cache := {}


static func bbcode_add_code_color(bbcode_text := "") -> String:
	if not _cache.has("regex_bbcode_code"):
		_cache["regex_bbcode_code"] = RegEx.new()
		_cache["regex_bbcode_code"].compile("\\[code\\].+?\\[\\/code\\]")

	return _cache["regex_bbcode_code"].sub(bbcode_text, "[color=#c6c4e1]$0[/color]", true)

@YuriSizov YuriSizov changed the title Node is too bloated for autoloads, any chance of static classes? Add support for fully static classes Feb 12, 2022
@Shadowblitz16
Copy link
Author

Shadowblitz16 commented Feb 13, 2022

You can have static classes, just not static properties.

oh really? thats cool.

Your static class can extend Reference or even Object, though in
oh really?

One problem is that the class keyword seems to still have predefined functions of object regardless if I derive from Refrence

@YuriSizov
Copy link
Contributor

One problem is that the class keyword seems to still have predefined functions of object regardless if I derive from Refrence

Reference extends Object. There is nothing smaller than object. What is your proposal about?

@Shadowblitz16
Copy link
Author

Its about bloat, I need a strictly empty static class, or be able to overwrite functions for auto loads.

@Rodeo-McCabe
Copy link

I need a strictly empty static class

Not sure if you know, but a script doesn't actually have to extend anything unless it's attached to a node or resource. You can have standalone scripts that don't inherit anything at all (but you can't autoload them because then they have to be a Node).

@YuriSizov
Copy link
Contributor

I need a strictly empty static class

Not sure if you know, but a script doesn't actually have to extend anything unless it's attached to a node or resource. You can have standalone scripts that don't inherit anything at all (but you can't autoload them because then they have to be a Node).

If you omit the extends keyword, the script extends Reference, which in turn extends Object, and their desire seems to be to extend something that is less than Object.

@Shadowblitz16
Copy link
Author

I need a strictly empty static class

Not sure if you know, but a script doesn't actually have to extend anything unless it's attached to a node or resource. You can have standalone scripts that don't inherit anything at all (but you can't autoload them because then they have to be a Node).

If you omit the extends keyword, the script extends Reference, which in turn extends Object, and their desire seems to be to extend something that is less than Object.

this is correct I basically need a empty object with just GC and type info

@jeancallisti
Copy link

jeancallisti commented Apr 21, 2022

This comment is not to discuss the request (not for or against it), simply to give a general tour of "static" in Godot from a person who is not used yet to the Godot ways. The goal here is to paraphrase the discussion and to give a tour of the general state of affairs, except with generalistic programming terms and concepts that can be understood by outsiders.

About singletons (not exactly about "static", but related) :

  • Godot is allergic to Singletons. They are named "autoload" singletons, but since a new instance of the node/script opens each time you double-click on it in the game editor, it's very easy to turn this so-called Singleton into just a regular class. Handy or nonsensical, you decide.
  • direct consequence: You can see the Autoload as some sort of factory for the instance of the autoloaded object. Except the Autoloaded class is still public so you can still create instances of it outside the factory.
  • Godot users genuinely don't understand why people want anything static or why they have issues with the so-called singletons. Not judging. That's just what I observed from reading the threads.

About static :

  • Godot has static functions
  • If you don't implement anything non-static into your Godot class (and if you don't derive it from a Node) then you're implicitly making it static. Side note: The "static" keyword on a class in other languages is just syntactic sugar to make sure the dev won't try to instantiate it by accident.

Back to static variables:

  • Godot doesn't have static variables , but since it has static functions (which are exactly the old-timey version of static property getters), then all you're missing is the container to store the unique instance of the so-called static variable.
  • Godot people recommend global variables, with the implicit understanding that you would store those in a Godot singleton , which means (as said before) really a global instance of a class, instantiated by the Autoload. All you have to do is pray that this instance remains unique and you got yourself your global variables, which implictly become static variables in this scenario.

As a conclusion :

  • If you do trust scripting languages and Godot, like Godot users do, then you can just assume that your godot autoloaded "singleton" instance is good enough. So, put your so-called global variables there, and since they're supposed to be 100% unique, just use your imagination and pretend that they're static variables.
  • If you don't trust Godot or yourself (a bit the same way that C# programmers don't trust javascript's loose typing) then do everything just like above (autoloaded class/node with global variables) but with the following extra safety : Create an accessor to it.
    In detail: Use static function to get the instance.
    In even more detail: Create a "static" class (for example, name it "Global"), i.e. a regular class containing a static function (for example, you could name it "get_singleton" ). Implement that function so that it gets the autoloaded "singleton" node by instance name (using the name from the Autoload panel) , to be 100% sure it's the one true instance, i.e. the one that you're after. In the end, you would do : Global.get_singleton().my_pretend_static_variable instead of $myAutoloadNode.my_pretend_static_variable

@Calinou
Copy link
Member

Calinou commented Jul 30, 2022

Closing in favor of #5025, which is more scoped to what would actually need to be implemented.

@Calinou Calinou closed this as not planned Won't fix, can't repro, duplicate, stale Jul 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants