Jump to content

  • Log In with Google      Sign In   
  • Create Account

D Bits



Dolce and Da5teroids

Posted by , in D, Dolce 18 December 2011 - - - - - - · 826 views
D, Dolce, Derelict
So I've finally gotten started on some game code. In order to see if Dolce is actually useful or a waste of time, I decided to start by porting the A5teroids demo that ships with the Allegro 5 package. Here's what I've learned so far.

First, the whole idea of Dolce as a framework on top of Allegro to allow an absolute minimum amount of startup code for a game was a good idea on paper. All of the initialization details were tucked away behind a single method call. In practice, it's rather silly. I was happy with the little example I gave some time ago. But, the number of options that need configuring in order to make it useful actually make it more complex than it would be if I didn't hide all of the initialization details. So, now I don't. Now, you initialize the modules you need as you need them. It's cleaner, still takes just a few lines to get to the good stuff, and makes more sense.

Second, I've forgotten how fun it is to make a game. Once I dug into the A5teroids source, I decided it's not something I really want to port. It appears to have been cobbled together with little thought or consistency. So I decided to mostly start from scratch. I'm using the same resources and related data, but that's it. I've not spent a whole lot of time on it so far, but I'm having a blast. Along the way, I've been tweaking Dolce to make it more useful. I've also begun to expand it a bit and start implementing some utility modules.

Finally, as a result of moving forward on all of this I've hit on some aspects of D that would make useful posts here. I've been rather quiet here for a while, so it will be nice to have something to say again.

I anticipate that over the next three or four weeks I'll be able to get a lot more work done in D-Land than I have up until now. Not only do I need to refine Dolce and prepare it for public consumption, I also need to get busy putting the finishing touches on Derelict 2 (which I've also finally decided to move to github, thanks to a bit of encouragement). Fun times ahead.


Dolce Gets Some Love

Posted by , in Dolce 17 November 2011 - - - - - - · 755 views
Dolce
Once the semester ended, I took a week to do nothing but play Dark Age of Camelot and get some much needed guitar practice in. Then I dove back in to the gym for some long overdue exercise. I also managed to pick up a few more private classes to fill some of the gaps in my schedule. Once I let off all that steam, I fired up VisualD and took a look at Dolce.

This has been an interesting project for me so far. In all of the years I've been programming, I've never experienced anything quite like this. On the surface, it's such a simple thing. To date, it's just a handful of D modules with a very limited interface. It's that latter bit which has been the challenge. I never realized how tough it is to design a minimal interface.

The goal with this project from the beginning has been to enable getting a game off the ground with Allegro and D quickly and painlessly. No wrapping of the Allegro libraries, no complex abstractions. Just a collection of routines to package up a bunch of boilerplate so that you can get to the game code almost as soon as you pull up your IDE for the first time. I thought it would take a few days of dinking around. After all, I've got years of experience under my belt. I could do this in my sleep, right?

From the very beginning, I stumbled over which approach to take with the interface: free functions or static class methods. Ultimately, I settled on the latter approach. Then I started implementing the outline I had sketched out. As I progressed, I realized it was a bit overly complex. Did I really need a templated resource manager for a finite number of known Allegro resource types? So I scrapped the first pass and went back to the drawing board. More than once. It's been a while since my last major rewrite, but I think there were a total of four. Some of that process was documented here on this blog.

Over the past couple of months, I've not done much more than a bit of tinkering here and there due to my busy schedule. But now that I've come back to the project in earnest, I can say it's looking good. I've got it to the point where it's ready to use for a game. And that is, in fact, my next step. I'm going to port over the A5teroids demo that ships with Allegro. And probably implement a few other old skool games just to get an idea of what else I need to add to the core.

The weird part for me is how much refactoring and thinking went into getting such a pitifully small amount of code put together. Of course, it would have been much easier had I been more familiar with Allegro in the first place. I used it a lot back in the late 90s, but that doesn't count given how long ago it was and how much Allegro has changed. Still, I will never again approach a "simple" interface with the idea that it will be simple to implement.

As the project moves forward, I'll be working on some utilities that could be useful for any game project, Allegro-based or otherwise. I'm looking forward to getting into some of the features of D I've not yet had much experience with, like ranges. Before all of that though, once I get a couple of small games knocked out and know for sure that the current interface achieves my goal, I'll do some proper DDoc comments and put all of the source up on Github.


On My Job, and Some Big D News

Posted by , in D, Dolce, Me 09 October 2011 - - - - - - · 827 views
Dolce, D, Me
Dolce is still very much alive, just getting little attention while I wrap up the second semester at the university. For anyone interested, I'm teaching in a special program where a local university here in Seoul is partnered with a university in America. The students do two semesters here in Korea before moving off to the States for three years. We started the second semester two months early this year in order to give the students more time between the end of the semester and their departure to America next January. Last year's students, the first to participate in the program, only had two weeks.

In addition to my Debate classes, I'm teaching a special English Fluency course this semester for the handful of students who have achieved their target TOEFL scores. Coupled with all of the private classes I teach six days a week, I don't have enough time to do everything I want to do. When my schedule gets full like this (which happens a couple of times a year), my D projects tend to suffer for it. I find it mentally tough to get any coding done if I can't work for long stretches at a time. I feel so unmotivated when it comes to short bursts at the keyboard.

Still, I've taken the time to do some nipping around the Dolce source here and there. Nothing major to report on that front yet. The semester ends for me in two more weeks, and I'm not giving my students a final exam this time around. So I'll find some nice gaps in my schedule soon that I'll use to give some attention to both Dolce and Derelict.

In the meantime, I'll leave you with this: a D compiler is now very close to being included in the GCC project. You might also be interested in checking out the related Reddit thread. That's great news all around. And I really need to get around to adding it to my D news blog.


Dolce Refinements

Posted by , in D, Dolce 04 September 2011 - - - - - - · 708 views
Dolce, D
In my last post, I showed the minimal amount of code needed to get something up and running with Dolce. And while it's a really small amount of code, something kept bugging me about the implementation. It just wasn't "D" enough.

Over the years, the lion's share of my programming experience has been with C and Java. When working with either language, I naturally, and effortlessly, use appropriate design patterns. In other words, I design "to the language." Maybe "design pattern" is the wrong noun here, but the point is that my code structure differs based on the capabilities and features of each language. And I can switch between them seamlessly, thanks to the years of doodling around I've got under my belt. My C code is "C", and my Java code is "Java". Unfortunately, I haven't quite gotten that line of clarity yet in D.

In D, it's possible to structure things like I do in C. It's also possible to structure things like I do in Java. That's why I've refactored Dolce so many times already. It's mentally distracting having both styles in the same code base. Unfortunately, I've found that sticking to one of the two styles is a source of annoyance, as I can't get over the feeling that if I'm writing Java or C in D, then why am I using D?

The approach demonstrated in my last post, where you subclass a Game class and override certain methods as needed, was obviously very Java-ish. And I didn't like the fact that I was requiring any methods to be implemented at all, even if there were only two of them. Finally, it seemed silly to me that if you want to avoid using the Game class, your only option was to handle all the boilerplate yourself. I knew I could do better than that.

So, I went back to the drawing board and came up with this.

import dolce.game.framework;

class MyGame
{	
}

int main(string[] args)
{
	Framework.start(new MyGame, "MyGame");
	return 0;
}

This will create a window that closes if the escape key or close button are pressed. Pretty useless in and of itself, but the point is that there are no longer any specific methods required and you don't need to subclass anything. In fact, you don't need a class at all. Change MyGame to a struct instead, and it will work fine (and you wouldn't need to 'new' it either, though you could if you wanted to). Even better, you can pick and choose the methods you want to implement.

Framework is a static class with a templated start method that looks like this:

static void start(T)(T game, string appname, string orgname = null)
{
	init(appname, orgname);
			
	static if(hasMember!(T, "init")) game.init();
			
	_running = true;
	al_start_timer(_frameTimer);
			
	while(_running)
	{
		// Pump all events.
		pumpEvents();
				
		// If it's time to update, do it.
		if(_update)
		{
			static if(hasMember!(T, "update"))  game.update();
			_update = false;
		}
				
		static if(hasMember!(T, "render")) game.render();
	}
			
	static if(hasMember!(T, "term")) game.term();
	term();
}

The thing I want to focus on is the hasMember template. It's implemented in the standard library module std.traits. It is not a function template. It takes a type list and no parameter list. The template itself contains one field, a bool value called hasMember. This calls for a brief detour to talk about templates in D.

Let's say I want to define a template that acts as a boolean value. Let's call it "isTrue".

template isTrue()
{
	enum bool val = true;
}

Here, the template has an empty type list and no parameter list. Internally, there is no function declared, only a single member, val (so it's not a function template). Notice that val is declared as an enum. In D2, single-member, anonymous enums are known as manifest constants. They are not actual variables, so cannot be used as lvalues or have their addresses taken. Essentially, every time the template is instantiated, that call to the template is replaced by the value of the enum.

Notice also that the template and the member have different names. This means that you have to explicitly type the member name when you instantiate the template. Also remember that D's template instantiation syntax is templateName!(). So, given that, this is how you would use this template:

void main()
{
	assert(isTrue!().val);
}

Sometimes you may want a template to have multiple members, but the large majority of templates you write are going to have only one. In that case, D let's you take some shortcuts if the member name is the same as the template name. In this case, we can eliminate the .val part when instantiating the template.

template isTrue()
{
	enum bool isTrue = true;
}

void main()
{
	assert(isTrue!());
}

std.traits.hasMember uses this approach. But it also takes two args to its type list -- one is the Type that it is to operate on, and the other is a string that will be used to look up a member method or variable of that type at compile time (this is accomplished via D's compile time reflection capabilities).

You may be wondering how a string value could be part of the type list, rather than the parameter list. The type list isn't just for types. You can also pass any sort of symbol that can be known at compile time. Let's change our isTrue example now to demonstrate this. The new job of the template is to evaluate to true if and only if a given string is equal to the string "true".

template isTrue(string s)
{
	enum bool isTrue = "true" == s;
}

void main()
{
	assert(isTrue!("true"));
	assert(isTrue!("bugaboo"));
}

Run this and the first assert will pass. The second will blow up. The string 's' is never used at runtime here. It's a compile-time-only symbol. When the template is instantiated, the compiler will run the test ("true" == s) and set the value of isTrue to the result. The asserts are basically being rewritten as:

assert(true);
assert(false);

By using hasMember the way I do in the Framework.start method shown above, no calls will be made to any methods not present in the given type. They simply won't be compiled in to the executable. If the method is implemented, a call to it will be compiled in. This means anyone using Framework.start can implement any combination of init, term, update and render methods.

One more thing to point out. Let's look at the minimal example again.

import dolce.game.framework;

class MyGame
{	
}

int main(string[] args)
{
	Framework.start(new MyGame, "MyGame");
	return 0;
}

According to the rules for instantiating templates, the call to Framework.start should look like this:

Framework.start!(MyGame)(new MyGame, "MyGame");

Again, the compiler is letting me take a shortcut. Because I only have one type in the typelist, I can omit the typelist and the ! when I instantiate the template. The compiler has all the information it needs to infer the type, so there's no need for me to be verbose about it.

With the new implementation, I managed to cut out several lines of code from the framework (previously 'game') module. And though I haven't implemented it yet, the door is now open to use Framework for handling the boilerplate if you want to use free functions instead of a class or struct. This is more what I would call "D style" than the first pass was.

Head on over to d-programming-language.org for more info on templates and the std.traits module.


Dolce and Overriding Methods in D

Posted by , in D, Dolce 21 August 2011 - - - - - - · 772 views
D, Dolce
I'm making very slow progress on Dolce, but progress nonetheless. I managed to grab a couple of hours today to refactor event handling and to simplify the framework interface.

In my initial implementation, it was necessary when making a game with Dolce to make all of the initialization and termination calls yourself. All of these calls are found in the different modules of the core package. They wrap a lot of Derelict and Allegro setup/cleanup in a way that is configurable, providing sensible defaults where required. But, while it's a good deal less boilerplate than you would otherwise need to type, it could be improved.

So, now, to start implementing a game with Dolce, the minimal amount of code you need is this:

module mygame.main;

import dolce.game.game;

class MyGame : Game
{
	protected override
	{
		string name()
		{
			return "MyGame";
		}
		
		void frame()
		{
			
		}
	}
}

int main(string[] args)
{
	Game.start(new MyGame);
	return 0;
}

Add your per-frame code in the frame method, and away you go. Both name and frame are abstract methods in the Game class, so they are the only two methods that must be overridden. I may yet rework it to eliminate the frame method and have a pair of render/update methods instead (haven't decided where or how to handle timing yet). But the point is, there's very little to do just to display something on screen. Of course, there are several other methods that can be overridden for game-specific init/cleanup, event handling, and more. But as is, this will call the frame method as fast as possible and has default handling for the escape key and window close button. And now this is starting to look like what I had envisioned in the beginning.

It's also possible to avoid the Game.start method and manage the life cycle yourself:

int main(string[] args)
{
	auto game = new MyGame;
	scope(exit) game.term();
	game.init();
	game.run();
	return 0;
}

Or, you can ignore the game module entirely and use the core package modules directly. Your choice.

The real point of this post, though, was a chance to talk about how D handles method overriding. Notice the override keyword in the first code block above. This is not mandatory when overriding, but it's a good idea to use it. DMD will spit out a warning if you don't. What it's good for is to protect against the case when the superclass changes. Imagine that you override a method from the Game class, but in the future that method is removed or renamed. By using override, the compiler will let you know the next time you compile that your method isn't actually overriding anything. Otherwise, it would be creating a new method in your derived class that doesn't override anything and you might never be the wiser until you get a funky bug in your app. So when writing D code, using override is a good habit to pick up.


D Structs vs Classes and a Simple Template Example

Posted by , in D, Dolce 02 August 2011 - - - - - - · 1,658 views

D shares a lot of similarities with C++ and Java, but a lot of the sameness is just a bit different. One of the first places new users see this is in the handling of structs and classes.

D's classes have more in common with those of Java than C++. For starters, they're reference types. Whenever you need an instance of a class, you new it and it is allocated on the heap (though scoped stack allocation is possible via a standard library template). Structs, on the other hand, are value types. But, you can create pointers to them, allocate them on the heap with new, or pass them by reference to a function that takes ref parameters. Another difference between the two is that classes have a vtable and can be extended. Structs, however, have no vtable and can not be extended. That also applies to the Java-esque interfaces in D -- classes can implement them, structs cannot.

Because of the differences between the two, there are certainly some design implications that need to be considered before implementing an object. But I'll talk about that another day. For now, I just wanted to give a little background before getting to an example illustrating the primary motivation for this particular post.

While working on Dolce, I thought it would be a good idea to wrap Allegro's ALLEGRO_CONFIG objects, simply because working with a raw, string-heavy C API in D can be a bit tedious. You have to convert D strings to C strings and vice versa. In this particular case, you also have to convert from string values to integers, booleans, and so on, since Allegro only returns raw C strings.

So what I wanted was something that allowed me to create and load configs, then set and fetch values of the standard built-in types. Rather than implementing a separate get/set method for each type, I chose to use templates. And in this case, I didn't want the whole object to be templated, just the methods that get and set values.

Initially, I implemented it as a class, but in the rewrite of Dolce I pulled the load,create and unload methods out and made them free functions. I also realized that this is a perfect candidate for a struct. The reason is that it contains only one member, a pointer to an ALLEGRO_CONFIG. This means I can pass it around by value without care, as it's only the size of a pointer. Here's the implementation:

struct Config
{
	private
	{
		ALLEGRO_CONFIG* _config;
	}
	
	ALLEGRO_CONFIG* allegroConfig() @property
	{
		return _config;
	}
	
	bool loaded() @property
	{
		return (_config !is null);
	}
	
	T get(T)(string section, string key, T defval)
	{
		if(!loaded)
			return defval;
		
		auto str = al_get_config_value(_config, toStringz(section), toStringz(key));
		if(str is null)
			return defval;
		
		// std.conv doesn't seem to want to convert char* values to numeric values.
		string s = to!string(str);
		static if( is(T == string) )
			return s;
		else
			return to!T(s);
	}
	
	void set(T)(string section, string key, T val)
	{
		if(!loaded)
			return;
		
		static if( is(T == string) )
			auto str = val;
		else
			auto str = to!string(val);
		
		al_set_config_value(_config, toStringz(section), toStringz(key), toStringz(str));
	}
}

You'll notice that the _config field is private. I don't normally make struct fields private, as the structs I implement are usually intended to be manipulated directly. But in this case I thought it prudent to hide the pointer away. I still provide access through the allegroConfig property (and I'll discuss properties another day) in case it's really needed.

So you may also be wondering how _config is ever set if it's private and there's nothing in the struct itself that sets the field. The answer lies here:

Config createConfig()
{
	return Config(al_create_config());
}

This is something that frequently trips up D newbies coming from other languages. What's going on is that the create function and the Config implementation are in the same module. You can think of modules as another level of encapsulation. And, for those who are steeped in C++ vernacular, you can think of modules as friends of every class and struct implemented within them. In other words, private class/struct members/methods are all visible within the same module. If you ever get to know D at all, you'll likely find this to be a very convenient feature.

Another thing that might jump out at you is the static if. This is one way to conditionally generate code at compile time. You'll frequently see it used in templates, though its use is not restricted to templates. Here, I'm testing if the type T is a string or not. In the get method, the value returned from Allegro is a char*, so it is converted to a D string. If the type of T is string, then there's no need for any further conversion and the D string can be returned. If not, the D string must be converted to the appropriate type (int, bool, long or whatever). Similarly, in the set method, a char* needs to be passed to Allegro, so any nonstring values are first converted to a D string. But if T is string, that step isn't necessary.

And now to the templates. D's template syntax is clean and extremely powerful. This example demonstrates the cleanliness part at least.

T get(T)(string section, string key, T defval)

If you've ever used C++ templates, it should be clear what is going on here. The type to be accepted is declared in the first pair of parentheses, the parameter list in the second pair. And in this case, a value of the specified type is returned.

Now, to use it:

auto config = createConfig();
auto i = config.get!int("Video", "Width", 800);
auto b = config.get!bool("Video", "Fullscreen", false);

Here, I've used the auto keyword for each variable. The compiler will infer the Config, int, and bool types for me. As for the template instantiations, notice the exclamation point used between the method name and the type. That's what you use to instantiate a template. Technically, I should be wrapping the type of the template in parentheses, like this:

auto i = config.get!(int)("Video", "Width", 800);

If you have a template with more than one type in the type list, then you have to use the parens. But if there is only one type, the compiler lets you get away with dropping them. For singly-typed templates, that has become a standard idiom in D.

If you decide to give D a spin and come from a C++ or Java background, I hope this post helps keep you from any initial confusion that might arise when you find that things aren't quite the same as you're used to.

You can learn more about D's structs, classes and templates at d-programming-language.org.


Dolce Progress, Importing D Modules in a Function, and WYSIWYG Strings

Posted by , in D, Dolce 31 July 2011 - - - - - - · 790 views

I've made a bit of progress on Dolce, but I realized something while I was doing it. My purpose for starting the project was to work on a game idea I've had for a long, long time. I knew from the get go that graphics were going to be a problem The problem is the open-endedness and complexity of the game experience. To pull it off, I either need very detailed graphics, or simplistic graphics with a good deal of descriptive text.

I'm no artist and I can't be bothered to pay anyone for the level of detail I would need for the first option. This iteration is just for fun, not profit. So I have to go for the second option -- minimal graphics plus text. My intention was to go very, very, simplistic. Not quite Dwarf Fortress simplistic, but very nearly. I had it in my head to use a simple tile set, with symbols of some sort for game entities.

My problem, as always, is time. Between work, family, and other projects that have higher priority, I'm wasting too much time on the graphical side of this game idea. I want to get busy with the game itself. Since I'll be using a good deal of text anyway, why not just ditch Allegro and go for a pure text-based game? So that's exactly what I've decided to do. Dolce, however, is a neat little idea that I hope to come back to now and again when I have a lull. And at some point I will very much want to make a graphical version of this game if it turns out to be as fun as I hope. But for now, text it is.

One of my first problems to consider is colored text in the console. I want the game to run on Windows, Mac and Linux, so I want something that's portable and simple. That means ANSI escape codes. But the Windows command console doesn't support ANSI escape codes right out of the box. After a bit of digging around, I found a solution.

ansicon is a little console for Windows that can understand ANSI escape codes. And it's freely distributable so I can bundle it with my app and use it in place of the standard command prompt. Of course, I wouldn't want the user to have to open it and type a command to run my app. Luckily, any arguments passed to ansicon that it doesn't recognize are considered to be a program to execute and its arguments. So a one line batch file would do the trick:

ansicon MyApp

Another option would be to make a simple executable to launch ansicon. In D, it might look something like this:

int main(string[] args)
{
	import std.process;
	return system("bin\\ansicon.exe MyApp");
}

Notice how I've got the import statement inside the main function. That's a new feature that was added in the latest DMD release. It doesn't seem like a big deal, but I can't count how many times I've knocked up a quick D script to test out some idea only to realize that I forgot to import std.stdio at the top. If I'm only using a symbol in one function, then instead of scrolling back up and adding the import at the top, I can just add it in place and drive on.

There are other functions in std.process that can be used to execute processes in a cross-platform manner. Of course, before someone points it out, I do realize that the path I passed is not cross-platform because of the backslash. Calls to system on Windows will fail if the path contains a forward slash, so you'd have to do the right thing based on platform. But for this launcher, I don't need to be cross platform.

For those who don't like escaping special characters, there's one more D feature that can help: WYSIWYG strings.

// Using r""
string s = r"bin\ansicon MyApp";

// Using backticks (not single quotes)
s = `bin\ansicon MyApp`;

D also supports heredoc strings, which also do not require escapes.

So now I'm on the road to making a complex text-based game to prototype my idea and you know a little more about D's strings.


D's Default Initializers

Posted by , in D, Dolce 02 July 2011 - - - - - - · 714 views
D, Dolce
I've been away from Dolce for a couple of weeks now. Just came back to it last night and realized I don't like it. I've horribly over engineered some of the modules. So from last night I started stripping stuff out and refactoring. In the process, I realized a silly mistake in my resource management code. I'm throwing it out and rewriting it anyway, but it inspired a topic for this blog.

My resource system was, overall, designed to work with any imaginable resource. So it's template-based and has a flexible interface. But I started from the perspective of Allegro resources, which means working with struct pointers. And I imagined that other potential resources that I might want to use would be class based. That led to an implementation detail that could cause compilation to fail in certain cases.

The problem boils down to something like this. Given a member called _resource of type T, I want to clear it out when I no longer need it in certain circumstances. Since I'm dealing with struct pointers and classes, which are always references, I can just do this:

_resource = null;

That works and does what I want. Then to determine whether a resource is loaded I can just test _resource for null. Until, of course, I decide one day to do something like use a struct resource by value, rather than as a pointer. Not something too farfetched. In that case, I'd get a compiler error. While DMD's template error messages are a good deal better than what most C++ compilers give us, it's annoying to get them. And there's no reason why I shouldn't support non-nullable types.

So here's a contrived example of what happens in this situation.

module nullify;

void nullify(T)(T t)
{
 	t = null;
}

void main()
{
	int i = 10;
	nullify(i);	
}

So nullify is a templated function with no constraints, meaning it can accept any type at all (I'll talk a bit about D's template syntax another day -- this is a specific case where you can declare the template without the template keyword and call it as you would a normal function). Try to compile this code and you'll get the following output:

nullify.d(8): Error: cannot implicitly convert expression (null) of type void* to int
nullify.d(14): Error: template instance nullify.nullify!(int) error instantiating

Right. To solve this problem, there are two obvious choices. One is to use template constraints to restrict the template only to pointers and classes. There would still be compiler errors of a different nature, but it would be a signal that this template is not intended to work with value types. In some cases, that might be preferable. In this particular case, a better option is to make use of default initializers.

Every type in D has a default value to which instances are automatically initialized on declaration. For example, ints are initialized to 0, floats to nan, classes and pointers to null. This value is readable as a property, .init, both on the type and on the instance. So we can modify the nullify template above like so:

void nullify(T)(T t)
{
	// You could use t.init or T.init here.
	t = T.init;
}

Now the code will compile. Pointers and classes will be set to null, floats and doubles to nan, characters to 0xff, and so on. What about value structs? Try this:

module nullify;

import std.stdio : writeln;
import std.string : format;

void nullify(T)(T t)
{
	t = T.init;
	writeln(t);
}

void main()
{
	struct Foo
	{
		int x = 10;
		int y;
		
		// Without a toString method, the name of the type
		// would be output by default in the call to writeln
		// above. In this case, "Foo".
		string toString()
		{
			return format("(%d, %d)", x, y);
		}
	}
	Foo f = Foo(23, 38);
	nullify(f);	
}

The output from this is "(10, 0)". Foo.y, as an int, has the default initializer 0. I've changed the default initializer of Foo.x, however, in the definition of Foo. So all instances of Foo will have the value 10 for x on instantiation. This is not the same as assignment. For example, this will not print 10, but 0:

int i = 10;
nullify(i);

Here, we are declaring an instance and assigning a value to this instance. We are not defining a type. Big difference.

So using default initializers is a convenient way to clear out a templated class/struct member for any given type. Then, tests like a hypothetical isLoaded method become

bool isLoaded()
{
	return _resource == T.init;
}

You can read more about the .init property in the D documentation at d-programming-language.org.


Dolce Progress and A Word on Mixins

Posted by , in D, Derelict, Dolce 11 June 2011 - - - - - - · 800 views
D, Derelict, Dolce
My time has been limited lately, but the core of Dolce continues to evolve. I think it's converging into something fairly decent. I'm still not completely happy with it, but I believe I'm most of the way there. As usual, I'm getting wild ideas for new projects. Like a renewed interest in building a 2D library from scratch in D. Or a more generic framework with pluggable backends for Allegro, SDL, SFML, and GLFW. You know, like I explicitly said at the beginning I had no interest in making. This time, though, I refuse to let myself get distracted. So I'm digging my heels in.

Recently, I've added event handling, mapping keycodes or mouse button ids to delegates. The first pass is usable, but it can be improved a good deal. D's delegates are great for this sort of thing. But delegates aren't what I want to talk about today.

Until now, my test app has only been using the keyboard. With the inclusion of the event handler, I added mouse support. While testing it out, I kept getting a puzzling result. Allegro has no constants for mouse buttons. They are numbered from 1 to whatever the maximum number is (which you can find out via al_get_mouse_num_buttons()). But my event handler kept getting button 0 passed to it, no matter which button I pushed. al_get_mouse_num_buttons was correctly reporting 3 (I dumped my 8-button gaming mouse some time ago). After several minutes of digging around, I started to suspect my DerelictAllegro binding. Sure enough, I had left out 4 fields from the struct declaration. I patched that up and all is well.

Looking at the binding source reminded me of a D feature that would be worth mentioning here. In my last post, I briefly mentioned D's string mixins. There is another type of useful mixin in D that I've made use of in the Allegro binding: the template mixin.

Allegro's event structs are all declared with the following macro at the top:

#define _AL_EVENT_HEADER(srctype)                	\
   ALLEGRO_EVENT_TYPE type;                      	\
   srctype *source;                              	\
   double timestamp;

So the mouse event structure looks like this:

typedef struct ALLEGRO_MOUSE_EVENT
{
   _AL_EVENT_HEADER(struct ALLEGRO_MOUSE)
   struct ALLEGRO_DISPLAY *display;
   int x,  y,  z, w;
   int dx, dy, dz, dw;
   unsigned int button;
   float pressure;
} ALLEGRO_MOUSE_EVENT;

D has no #defines, and no preprocessor at all. But this C idiom can easily be replicated with template mixins. Here's what I did in the DerelictAllegro binding:

// First, declare the template that you intend to use as a mixin.
template _AL_EVENT_HEADER(T)
{
	ALLEGRO_EVENT_TYPE type;
	T* source;
	double timestamp;
}

// Then, mix it in.
struct ALLEGRO_MOUSE_EVENT
{
	mixin _AL_EVENT_HEADER!(ALLEGRO_MOUSE);
	ALLEGRO_DISPLAY* display;
	int x, y, z, w;
	int dx, dy, dz, dw;
	uint button;
	float pressure;
}

Simple. String mixins could also be used here.

 // You would want to use a compile time function with a signature something like this
string allegroEventHeader(string type) { ... }

// Then call it like so:
mixin(allegroEventHeader("ALLEGRO_MOUSE"));

Personally, I prefer template mixins in situations like this.

Take a look at the template mixin documentation over at the shiny, new d-programming-language.org website for more info.


Operator Overloading in D

Posted by , in D, Dolce 29 May 2011 - - - - - - · 1,335 views
D, Dolce
I didn't add much of anything new to Dolce in the past week. The time I had to work on it was mostly spent refactoring. I'm patching this thing together haphazardly as I go along. I have a good idea of what I want it to do, but how to organize it all wasn't clear from the beginning. Package hierarchy, structs vs classes, module content, and so on. My little test app has served two functions: making sure it all works and understanding how it will be used. It's the latter that led to the refactoring.

So now that the plumbing is done and in working order, it's time to start looking at game-related utility code. For that, my little test app is woefully inadequate. So I'll be porting the a5teroids demo that ships with the Allegro package. This will get me started. But before I begin the port, I'm looking at two additions: entities and a math package/module. Since entities will likely need the math, I'm starting with that.

I'm not yet sure if I want a whole new package for the math stuff or just a single module, but for now I'm going with the former. The first module in the math package provides an implementation of a 2D vector. It's been a while since I've done any operator overloading with D, and I had completely forgotten how different it is between D1 and D2.

In D1, operator overloading is similar to the way it works in C++. The biggest difference is that instead of overloading by symbol, you overload by purpose. The following overloads the '+' operator.

struct Foo
{
	int x;

	// Foo + int
	int opAdd(int i)
	{
    	return x + i;
	}

	// int + Foo
	int opAdd_r(int i)
	{
    	return i + x;
	}
}

The rationale behind naming overloads based on purpose, rather than the symbol, is that it encourages programmers not to change the meaning of the symbol. This way you can expect Foo + Bar to actually do some sort of addition, rather than an unrelated operation. I thought it was a pretty good idea when I first saw it. Whether or not it actually meets that goal in practice I couldn't say. Regardless, I'm not using D1, and D2 has changed this dramatically. For more on operator overloading in D1, see the D1 documentation.

In D2, most (not all) operator overloads are templated. You have a few basic templates that can be constrained based on the type of operator you want to overload. opUnary is for unary operations, such as negation. opBinary is for binary operators, such as addition. You can implement the templates once for each operator, or combine several into one instance, using template constraints in both cases. The following example demonstrates..

bool isMathOp(string op)
{
	return op == "+" || op == "-" || op == "*" || op == "/";
}

struct Foo
{
	int x;
	
	// Overload the negation operator '-'
	int opUnary(string op)() if(op == "-")
	{
        	return -x;
	}

	// Overload addition, subtraction, multiplication and division for the case of Foo op int.
	int opBinary(string op)(int i) if(isMathOp(op))
	{
		mixin("return x" ~ op ~ "i;");
	}
	
	// Overload the same for the case of int op Foo.
	int opBinaryRight(string op)(int i) if(isMathOp(op))
	{
		mixin("return i" ~ op ~ "x;");
	}
}

This code example uses two other nifty features of D. isMathOp is a function that qualifies for execution at compile time (the reasons for which I may talk about in a future post). Compile-time function execution (CTFE) is an awesome tool for generating code. In this case, it's not particularly needed, but it does make the template constraints more concise. Plus, I wanted to show off.

The second feature is string mixins. A string mixin basically inserts the given string into the code during compilation. In this case, it is generating strings of code like "x + i" or "x - i", for each template instance, using the append/concat operator. It's possible to call CTFE functions inside a mixin if you need to. That's a technique I use in Derelict to ensure compatibility between D1 and D2 (Dolce is D2 only, but Derelict has to support both).

So you can use CTFE and mixins, together with template constraints, to cut down the amount of code you need to implement in order to overload operators in D2. There are several more options for operator overloading, including some that aren't template-based. You can read more about in the D2 documentation.

Now, back to work on Dolce.






September 2016 »

S M T W T F S
    123
45678910
11121314151617
18192021222324
252627 28 2930 

Recent Entries



PARTNERS