In my previous post, I talked about Dolce's game screens and the stack-based screen manager. Internally, the stack that the screen manager uses is not a custom-built stack class, nor any sort of container from an existing library. It's an array. You can implement a stack quite easily with a D array.
// Declare an empty dynamic array to use as your stack.
Item[] stack;
// To push an item onto the stack, use the array append/concat operator.
stack ~= something;
// To pop, simply decrease the length of the array.
stack.length -= 1;
// What if we have a queue instead of a stack and want to take the first item?
// First, save the item at the head of the queue.
auto item = queue[0];
// Then reassign the array to a slice of itself, starting from index #1 to the last index.
// The $ is a substitute for the length property of the array. The first number in a slice
// is inclusive, the second exclusive. So the resulting slice below will contain all elements
// from queue[1] to queue[queue.length - 1].
queue = queue[1 .. $];
D's arrays are quite powerful. In addition to being arrays, they are also ranges, which is something that would take more than a simple post to explain. I don't quite fully grok them myself yet. But, when used in conjunction with the std.algorithm module, there's a great deal of power behind them.
You can read more about D's arrays in this article on slicing in D by Steven Schveighoffer. Also, see the documentation for the std.range module to get an idea of what ranges are.
But wait, there's more! The registry the screen manager uses is a built-in associative array. While there are some more sophisticated hashmap implementations out there for when you really need them, the built-in AAs will cover a lot of your use cases.
// Declare an AA that uses strings as keys.
Item[string] registry;
// Add an item.
registry["foo"] = myItem;
// Remove an item.
registry.remove("foo");
// Test if an item exists in the AA .
// The in operator returns a pointer.
auto item = key in registry;
if(item !is null)
// Do something
// Efficiently iterate all the values via a delegate
foreach(val; registry.byValue())
writeln(val);
// Or the keys
foreach(key; registry.byKey())
writeln(key);
// But don't try to remove anything while iterating the delegates as above. Instead,
// iterate over an array of keys or values using the .keys or .values properties.
foreach(key; registry.keys)
{
auto item = registry[key];
if(item.removeMe)
registry.remove(key);
}
D's arrays and AAs will get you farther than those of C++ or Java. But, when you need more, there is also a range-based container module, std.container, which will eventually hold a number of container types. Also, as an alternative, Steven Schveighoffer's DCollections library is quite awesome.
One thing I really don't like about D's arrays is the fact that array literals are allocated by the GC, even when initialising a static array:
int[3] a = [1, 2, 3]; // performs GC alloc then copies the elements into the array
As far as I can tell, there is no simple (built in) way to initialise a static array without a heap allocation. You either have to do it manually:
// yuck!
int[3] a;
a[0] = 1;
a[1] = 2;
a[2] = 3;
Or I suppose you could write a funky variadic template function for it, but I can't figure out the syntax for that (or even determine if it's possible).