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

Dictionary class is missing key methods #1337

Open
limbonaut opened this issue Dec 16, 2023 · 7 comments
Open

Dictionary class is missing key methods #1337

limbonaut opened this issue Dec 16, 2023 · 7 comments
Labels
discussion enhancement This is an enhancement on the current functionality

Comments

@limbonaut
Copy link

Godot version

4.2.1-stable

godot-cpp version

4.2

System information

Manjaro GNOME, NVIDIA X11, Forward+

Issue description

Dictionary is missing get_value_at_index, get_key_at_index and get_valid.
get_valid is easy to work around, but the other two are problematic. The workaround if to get keys() and values() to iterate over, and it's slow.

Steps to reproduce

N/A

Minimal reproduction project

N/A

@AThousandShips AThousandShips added the enhancement This is an enhancement on the current functionality label Dec 16, 2023
@AThousandShips
Copy link
Member

AThousandShips commented Dec 16, 2023

These are not exposed, and Dictionary is not a declared type on the godot-cpp side, so this is a limitation on the binds side, just like this isn't available in GDScript

This would require either declaring Dictionary on the godot-cpp side, or exposing it to the binds, i.e. exposing it to GDScript as well

How is iterating over the keys and values slower? How have you confirmed that this would be slower than using the non-exposed methods? Given the implementation in c++ the get_key/value_at_index methods are pretty slow:

	int index = 0;
	for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
		if (index == p_index) {
			return E.key;
		}
		index++;
	}

	return Variant();

It iterates over the map, which is a hash map, so the time for each operation is O(n), so nominally using this to iterate over the dictionary is O(n^2), copying the keys and values and iterating over them instead is most likely faster, quite a bit faster in fact

Instead doing fetching just the keys, and then iterating over those, is nominally an O(n) operation, fetching the keys is O(n), and looking up each key in the dictionary is amortized constant

@limbonaut
Copy link
Author

I assumed it's slow. You are right, for iterations it should be faster to use keys()/values(). I guess they are not important enough to expose since edge cases, when you need to get a single key/value at a certain index, are rare.

@AThousandShips
Copy link
Member

AThousandShips commented Dec 16, 2023

Well I'd suggest confirming it's slow before opening an issue 🙂, assumptions are dangerous when they inform decisions like these

You can still get the key and index as a specific index though, with those methods getting the keys and values, and they aren't meaningfully slower when doing them once (just "I want this specific entry", then just do dict[dict.keys()[i])), and if iterating it's actually faster as explained above 🙂

I'd say a more useful improvement would be to add enumeration, i.e. begin/end such as is available on the packed arrays, which GDExtension has support for (as you can do it in GDScript), which would be equivalent to for (const Variant &key : dict.keys()) but without having to produce the array first

@limbonaut
Copy link
Author

limbonaut commented Dec 16, 2023

Enumerations would be great! 👍
My main reason to open this issue was that they are absent from the GDExtension API, for compatibility reasons. I'm considering porting a module with a support for compilation as both GDExtension and as an engine module. But the workaround is good enough actually faster.

@AThousandShips
Copy link
Member

AThousandShips commented Dec 16, 2023

Do try the following for enumeration, from the example code:

Variant test_variant_iterator(const Variant &p_input) {
	Array output;

	Variant iter;

	bool is_init_valid = true;
	if (!p_input.iter_init(iter, is_init_valid)) {
		if (!is_init_valid) {
			return "iter_init: not valid";
		}
		return output;
	}

	bool is_iter_next_valid = true;
	bool is_iter_get_valid = true;
	do {
		if (!is_iter_next_valid) {
			return "iter_next: not valid";
		}

		Variant value = p_input.iter_get(iter, is_iter_get_valid);
		if (!is_iter_get_valid) {
			return "iter_get: not valid";
		}
		output.push_back(((int)value) + 5);

	} while (p_input.iter_next(iter, is_iter_next_valid));

	if (!is_iter_next_valid) {
		return "iter_next: not valid";
	}

	return output;
}

This could be made more convenient, but at the moment I don't think there's an extension detail to indicate that a type is iterable, so would need that I think

Edit: Will do some testing later to more easily do this as a for loop, should be trivial, will see though, could be able to also create a macro for ease, such as GODOT_FOR_EACH which takes something like:

#define GODOT_FOR_EACH(m_variable, m_iterable)

Which then would be used so:

GODOT_FOR_EACH(foo, iterable) {
   bar(foo);
}

@limbonaut
Copy link
Author

Ideally, it would be nice to have a common iteration solution for Dictionary in both the engine module code and GDExtension code. Less #ifdefs. I'll stick to keys()/values() from now on, but for(kv : dic) would be a nicer and cleaner alternative.

@dsnopek
Copy link
Contributor

dsnopek commented Dec 21, 2023

I'd say a more useful improvement would be to add enumeration, i.e. begin/end such as is available on the packed arrays, which GDExtension has support for (as you can do it in GDScript), which would be equivalent to for (const Variant &key : dict.keys()) but without having to produce the array first

Being able to iterate over Array like this would be really cool, and probably not even that hard to add!

However, since we try to match the API of the engine itself, we'd need to first add it to Array in Godot before we could add it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion enhancement This is an enhancement on the current functionality
Projects
None yet
Development

No branches or pull requests

3 participants