Upcoming Events
Tokyo Game Show
10/9 - 10/12 @ Tokyo, Japan

IndieCade
10/10 - 10/17 @ Bellevue, WA

Blizzcon
10/10 - 10/11 @ Anaheim, CA

2nd European Conference on Games Based Learning
10/16 - 10/17 @ Barcelona, Spain

More events...


Quick Stats
3681 people currently visiting GDNet.
2222 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

  search:   

A Postmortem of Game Programming with Digital Mars’ D Programming Language



Contents
  Prologue
  Installing
  Getting work done
  My experiences
  Beginning work: Dir2
  DERELICT game library
  Various Notes on D
  Wrapup

  Source code
  Printable version
  Discuss this article

Various Notes on D

writefln

writef is a cool function. If you are looking for control, it works just like printf.

writef(“%d + %d is %d\n”, 1, 1, 1 + 1);
However, if you are in a hurry and want a bunch of debug stuff dumped out:
writef(filename, “ “, line_number, “\n”);
Even better: since every printf I’ve ever written in C has had \n tacked to the end of it:
writefln(“%d + %d is %d”, 1, 1, 1+1);
writef is type safe. It auto-detects sign, so no more %u / %d nonsense. You can also use %s for every insertion and D will lexical cast everything into a string before displaying.

Strings

In D, strings are a built-in part of the language. But it’s better than that. In D, arrays are a first-class part of the language. Strings (being arrays of characters) benefit from all the cool things you’ve ever wanted arrays to do.

return array[4..length]
This is possibly one of the coolest single lines of code I’ve ever seen. It’s free! Because I’m making no modifications, but am just returning a slice of an array, the line of code above uses 8ish bytes of memory and no processing time. Also note that length is automatically scoped inside array.

Unit Testing

On these first two projects, I didn’t use D’s built-in unittest keyword . Later, on another project, I decided that I should toss in some tests. unittest{} is very well written. Anywhere in your files, you can drop a unittest{} block (treat it like a self-calling pseudo-function (if you’re from C++)) and those tests will get executed when you run a –unittest build. It is a simple written system that works well. I don’t normally unit-test my code, but with as easy as D makes it, I can see the benefits. After filling out one of these blocks, I know my code is not broken in several ways. I also know that it cannot become accidentally broken, because my tests will demonstrate that. I found this pattern throughout my experience with D. D makes it easier for me to be the kind of programmer I would like to be.

Unfortunately, the simplest version assert is about the only built-in testing function. More about that next.

Code generation

In C, macros are seriously powerful tools. I don’t accept the arguments of people that macros are ugly, type-unsafe, code-unfriendly, and un-debuggable. That is all perfectly true, but in C/C++, there are jobs which can be done either only by macros, or can be done best by macros. Inline templated functions help, but macros still fill a niche. D doesn’t have a macro preprocessor. There’s static if, static assert, templates, delegates and mixins. The big point here is that there isn’t a separate macro language you have to learn. The entire D language is available to you AT COMPILE TIME! (There are also nested functions, which help alleviate the macro problem, but that’s a different topic.) Unfortunately, D ends up being short a little functionality by removing the full preprocessor. After working with the language a little, I wanted to add a more comprehensive assert_equal(param1, param2) to my programs. This construct is very useful for unit testing, but ends up being very ugly or unhelpful without full support. In C, this function is implemented as:

#define assert_equal(param1, param2) \
  do {	 \
    if((param1) != (param2)) { \
      printf(“Test %s == %s failed.\n”, #param1 #param2); \
      printf(“%s != %s\n”, as_string(param1), as_string(param2) ); \
      assert(0); \
    } else { \
      printf(“Test %s == %s passed.\n”, #param1 #param2); \
    } \
  } while (0) 
This has the problem of evaluating the params too many times, but gets close. The important things that I want here are that the text of the test (Test obj.name == foobar passed) and the reason for the failure (bazdoo != foobar) are both printed. Also, when this code is run in a debugger or the assert(0) line fires, I want the file / line number reported to be that of the client code, not the file location of the macro. I have made several attempts at this and failed one of my three requirements. Using mixins (evaluate a string as code at compile time) is ugly. Instead of assert_equal(obj.name, “foobar”), I end up with mixing assert_equal(“obj.name”, “\”foobar\””). This code also doesn’t report the failure line correctly, and I couldn’t get a __LOCATION__ (defined as __FILE__, __LINE__) compile time variable to work. Using a normal function prevents me from reporting both the text of the test and the correct failure line. For example broken code, see the appendix.

Documenting

Built-in source documentation. I realize Java and C# and other development platforms offer this. D is nice in that this is part of the basic compiler (add one flag and you get a nice HTML file showing your documentation) and that it was designed in from the start. Further, the documentation thing isn’t some separate source file based xml parser: It’s part of the same language. (CONSISTENCY!) Add an extra trailing comment character to your comment:

/// I’m a documentation comment
/** so am I */
/++ and so am I! +/
… and the compiler does the rest. Because this is built-in to the language and is so easy to use, you’ll use it too. There isn’t a separate mark-up language you have to learn (though there are some features if you care) it’s all part of D, and D is a clean language. Using CandyDoc as part of your project gives a nice sidebar and some syntax coloring.

Automatic variable typing

autos are really, really nice. In C++, if you want to iterate across a list, you have to do some nonsense like:

for(list >::const_iterator iter = my_list.begin(); iter != my_list.end(); iter++) …
D recognizes that this is complete nonsense. The compiler already knows what type my_list is of.
for(auto iter = my_list.begin(); iter != mylist.end(); iter++) …
And because D has a for-each:
foreach(auto iter; my_list) …
Call me crazy, but that looks like something C++ should have had all along.

autos are also nice for weird functions where you just need the return value of a function.

auto object_of_some_oddly_named_return_structure = unstd.odd.count_fibwotzes();

Problems with object pointers

I had noticed earlier that D doesn’t have the -> or * operators. The language seemed to really downplay the differences between variables, references, pointers and smart pointers. I thought that it was a nice touch, since the compiler knows what each type of variable is and doesn’t need a syntactic hint. However, I got several problems trying to build my objects and later in building a vector class for my objects. D objects are all dynamically allocated – always.

my_class my_object; // This is a pointer thingie initialized to null
my_struct my_struct_obj; // this is a struct object – actual variable
my_struct *my_pointer; // this is a pointer initialized to null
my_class my_real_object = new my_class; // this is a class object ready to use

my_object.do_stuff(); // crashes
my_struct_object.do_stuff(); // fine
my_pointer.do_stuff(); // crashes, but you knew that
my_real_object.do_stuff(); // fine
The next thing that bit me was that since every class object MUST be dynamically allocated, there is no way to define that every object has an internal position vector (using class) defining its position. I had to change the vector type to struct and it was fine. I seem to remember Java works this way, and it’s an important difference I overlooked when starting.

I also had trouble checking object pointers against null. I hated this one. Because the difference between pointers, references and object variables is mildly hidden, checking an object for null is different than in C.

if(my_obj == null) { // Crashes!
That line of code calls the virtual opEqual function defined for my_obj (which might be null) Trying to grab addresses off of my_obj is just wrong.
if(&my_obj == null) { // JUST PLAIN WRONG
This won’t crash, and will never return false - & is checking the address of the reference variable my_obj, not its contents.

Apparently, the right way is to use a special is operator for this.

if(my_obj is null) { // yeah!
I also got some mileage out of ! and direct eval but I like is better.
if(myobj) {
if(!my_obj) {

Object member functions

As I build my classes, in this project particularly the vector class, I generally don’t add member functions or functionality until I’m actually using it in the client code. D’s operator overload is a bit different than C++’s because the operators are all named as functions (opMul, opAdd), instead of as operators (operator*, operator+). This adds clarity as to what the functionality being added is, instead of drawing attention to the specific syntactic sugar the language will let you use in calling. I think this naming scheme (while requiring a function name look-up to add operators) also reminds users that these functions can be used in callbacks.

Nested functions

Nested functions are cool. You can use them both as parameters for delegates and as static nested functions they are a nice way to handle simple topical tasks. In C++ I would have either done a huge block of logical statements or a separate static function placed earlier in the file. This moves the control logic away from where it is actually used. In D, you can define a little utility function (with or without access to all the variables of the parent function) and use it multiple times thereafter. One way to think of non-static nested functions is like class member functions, except instead of a hidden data pointer to an object of that class being passed to the function, there is a hidden data pointer to all the local variables of the parent function.

with keyword

Have you ever programmed in BASIC or Pascal? Did you then move to C/C++ and wonder how those programmers ever got any work done? What is it with those programmers and their dot-target fetishes? ( “.” or ”->” ) That’s right. C/C++ is completely missing the with/using keyword. Member functions alleviate some of that problem, but in the same way that C doesn’t have nested functions to let you simply and directly factor out coupled but duplicated code, C doesn’t have a way to get rid of those nasty pointers.

Here’s how it works for everyone who hasn’t seen this yet. In C/C++, you are continually typing

my_object->member_pointer-> silly_array[my_object.iterator].member_1 = …;
my_object->member_pointer-> silly_array[my_object.iterator].member_2 = …;
my_object->member_pointer-> silly_array[my_object.iterator].member_3 = …;
	… ad nauseum …
Of course, you get tired of that and use a cached pointer.
my_pointer = &my_object->member_pointer-> silly_array[my_object.iterator];
my_pointer->member_1 = …;
my_pointer->member_2 = …;
my_pointer->member_3 = …;
But honestly, that’s not any better. If you have a lot of work to do down in the elements of silly_array, that pointer is going to be thrown everywhere.

Enter with. This was an essential keyword in Pascal, and I don’t know how I’ve lived without it in C++.

with( my_object->member_pointer-> silly_array[my_object.iterator] ) {
  member_1 = …;
  member_2 = …;
  member_3 = …;
}
Now isn’t that a lot better?

Libraries

D is really cool because it is link compatible with all C libraries ever written (if they use the gcc compiler). There’s a bit of work to get the externing and DLL linking (such as Derelict does) working, but all those libraries written in the past for C will work for D as soon as someone writes a new header file (essentially) for D.

On another project, I started trying to write win32 apps using D. I found that the Phobos library std.windows was missing a lot of the win32 library. It was very simple to use the MSDN documentation to get C function prototypes for each function I needed and then add them to my project. D automatically linked to the windows libraries.



Wrapup