godot: Callable unbind doesn't actually unbind arguments

Godot version

4.0.2

System information

Linux 5.10.167-2-MANJARO

Issue description

Calling unbind on a callable doesn’t actually unbind the arguments. However it makes godot think the arguments are unbound, thus requiring you to call the callable with fewer arguments. But some of the arguments passed along in the call will be ignored, and the original bound arguments are used instead.

Steps to reproduce

Run

func foo(a: int, b: int, c: int):
	print("a: ", a)
	print("b: ", b)
	print("c: ", c)

func _ready():
	var callable = foo.bind(1,2)
	callable = callable.unbind(1)
	callable.call(4,5)
	# errors
	# callable.call(4)

The printed output is:

a: 4
b: 1
c: 2

Whereas id expect to see something like

a: 4
b: 5
c: 1

Minimal reproduction project

N/A

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 18 (16 by maintainers)

Most upvoted comments

I mean unbind does not seem to be an inverse to bind but the complement, it doesn’t undo bind, it ignores arguments from the user, so it is the complement to bind

I’d say it isn’t a bug but does cause confusion sure, also the use here isn’t really something that’s normal to do? Why would you remove bound arguments, like if you were given a callable from somewhere and don’t have control over it

My bad I forget where I saw it used before, might have gotten it confused

The implementation actively ignores called arguments, which seems to be the intention of unbind, it’s not the reversal of a previous bind, it just ignores excess arguments which is helpful in a number of situations, here’s the implementation https://github.com/godotengine/godot/blob/a7276f1ce0c2911216a2c4718efddab98ddffd8f/core/variant/callable_bind.cpp#L242

Usecase for this is connecting to a signal that has some argument

This is also documented imo quite clearly:

Calling the returned Callable will call the method without the extra arguments that are supplied in the Callable on which you are calling this method.

To achieve this behavior (for callables that are actual named functions) you can do:

callable = Callable(callable.get_object(), callable.get_method()).bindv(callable.get_bound_arguments().slice(1))