Jump to content

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

code language documentation file nice files line function want
A detailed look at getting set up and started with D, as well as various highlights and pitfalls of the language

4: Adsense

One thing I find very interesting is different computer languages, their development and their differences. I spend most of my time in C++, but the little exposure IÆve had to LISP or to other languages always leaves me wishing that CÆs macros were cleaner, or that C++ had a way to implement a nice multi-variable for-each.

This past month IÆve been on vacation and since my wife would be bringing her laptop, since I didnÆt feel like bringing the MSVC++ CDs or backing up my projects directory, and since I didnÆt really have any current project I wanted to work on, I decided to break out Digital MarsÆ D Programming Language and try to learn it.

I had read through some of the first pages of Digital MarsÆ DÆs documentation and propaganda, but I had never taken the time to actually work with the language. After a few weeks of fiddling with it when not at the pool, I have to say I really like the language.

Two major design philosophies that I feel when working in D are: 1) Things that you do a lot should be simple and 2) A language should be clean, simple and consistent.

This diary is about the specific joys and difficulties of working with D.

D is billed as a ground up rewrite of C û a new version of a good language, or everything that C++ wishes it could be but never will be.

Complaint #0: ôDö is horrible to search for. ôC++ö is a distinct string. I ended up having to use ôD Programming languageö or ôDigital Mars Dö every time I wanted results. Searching from within DigitalMars.com and then forcing Google to use the within domain and the ô-archiveö flag helped when I wanted language specific answers.

Google is everyoneÆs best friend. ôD Programming Language Downloadö drops you to a page with all the downloads. I was running Windows XP Pro in the hotel room, so I grabbed DMC.ZIP and DMD.ZIP. (The Linux people will want to grab the DMD.ZIP for Linux and must have GCC lying around. Presumably youÆll know where to find that. ) You might also want to find CandyDoc because itÆs pretty.

ItÆs a quick download, which is nice because IÆm on dialup at home.

I initially wanted to run the programs from the desktop, so that I could easily clean everything off when I left the hotel. That didnÆt work out in the brief time I spent fighting, but the following files seem to be important if you want to bash D into running from a non-root location: DMD\bin\sc.ini and DMD\source\d\all.sh. ThereÆs probably more to it, but I gave up. At least some of the tools donÆt handle file paths with spaces in them.

I still didnÆt want several folders littering my C: root, so I extracted the DMD.zip into c:\DMD and the DMC.ZIP into c:\DMD\DMC. This required modifying the LINKCMD in DMD\BIN\SC.INI to reflect the moved DMC folder. (On a second attempt at home, I extracted everything into c:\dmd\... \bin, \src, \include. You have to update sc.ini again to reflect D's settings, but it worked.)

I created a projects folder under c:\dmd\projects.

Getting work done
I like using Textpad as my editor with a modified D.syn file. The standard Textpad download D.SYN is missing some keywords. I also opened two cmd windows with the following batch file

@ECHO OFF SET PATH=%PATH%;"C:\DMD\bin";"C:\DMD\dmc\bin" CMD ; Run a command line and don't return (At home, I don't need the c:\dmd\dmc\bin, because I successfully flattened DM-C and DM-D into the same set of directories.) The batch file was configured to run in c:\dmd\projects.

My first cmd window was used for compiles and my second was used for running my programs. This resulted in some mouse clicking, but left everything open. Both BUD and dmd also have the "execute on successful compile" option, so having just one cmd window works as well. I also opened folder windows to the Phobos standard library source and to the documentation and had Textpad taking up + the screen.

Posted Image A good test of whether your install worked is to run your command window, then navigate to c:\dmd\samples\d\ and run:

shell all.sh If it works, D is configured and installed correctly. If it doesnÆt, you failed somewhere. It took me a few tries at home and 1-2 tries at the hotel to get things up and going.
My experiences
The online documentation, a lot of which is installed with the compiler, is very readable. IÆm a bit odd, but I found the documentation to be enjoyable reading. (I find Microsoft help to be useless and Microsoft online documentation to be needlessly obfuscated. (UnixÆs man help pages are alright. They are uniformly terse, but are still complete and tend to actually answer the questions I have .) After I started working with the D compiler, I found that Digital Mars D Documentation suffers from two inexcusable lacks. First, there is no comprehensive index or table of contents. All month, IÆve been struggling with 2-3 recurrent problems with features which I want to use, but donÆt remember how they worked. IÆve been searching the documentation for ôconstö and ôfunction parameters" numerous times, but since Digital Mars uses Google as their index front end, the results are all original discussions of the feature in the forum archives (filled with people much smarter than I am) and buried somewhere is the actual documentation page. Continually searching for ôsite:digitalmars.com ûarchive class objectö improves the results.

Request #1: The D Programming Language Documentation needs to be comprehensively indexed û both by subject and by concordance keyword.

The second lack in the documentation is its terseness. (This may be aggravated by the difficulty I found in finding answers). The documentation seems to be written by extremely smart people to explain to other extremely smart people the differences between C++ and D. (Or written to explain to a writer of another D compiler exactly how to implement it.) I would prefer that some things that should be obvious or are explained under another topic be spelled out in their own page. Something more on the lines of Programming in Lua by those guys, or The C++ Programming Language by Stroustrup. There are answers to many questions in the documentation, but you must randomly search for it. Only a little of the documentation appears to be available from the D Programming Language home page.

Request #2: The documentation needs to be doubled: preferably by explaining more things, but perhaps by merely storing related topics on separate pages with footer linking.

Remember, I said I found the documentation to be an enjoyable read. Since D has built in support for code auto-documentation, the library documentation tended to be complete. It also had a comprehensive table of contents as a sidebar. Again, repeatedly listing requirements and error cases would be nice for each function, instead of writing the doc once per module, but again this is a language written by very smart people for very smart people.

Beginning work : Dir2
I started by grabbing the ôhello worldö or ôword countö app, I forget which. I copied it from \samples to \projects, changed the documentation to reflect that I was now its author and started work.

IÆve wanted a nice directory diff tool for a while now. I realize that there are many of these available, but IÆve always been busy when I needed one, IÆm too cheap to spend the $20 for a commercial one and too lazy to search for a good open source one. Besides, not finding a good tool has left me this opportunity to write one. When I got to the hotel, I immediately copied my 1000 crappy 80Æs games CD on to the computer and Windows copy barfed after 975 of them. This left me with no good way of knowing which of the 100,000 files the problem was, but I tried to manage. This is a perfect illustration of one application of my directory diff tool. I want to be able to repair large directory copies, not merely delete and retry.

The first job is to write a tool which can list all files recursively across a folder and report the findings. D has a function with this functionality; listdir. I immediately encountered the unacknowledged recursive NTFS listdir bug. In the D archives, thereÆs a complaint that listdir doesnÆt always recurse. The response is the classic ôIt works on my machine." On the hotel computer, I had this same problem. I donÆt know why, but I had to write a listdir which could recurse. (I later encountered a copy-file problem: std.file.write( out_filename, std.file.read(in_filename)); worked fine, but copy_file(out_filename, in_filename); didnÆt. Ah, turns out that the parameters are SRCPATH, DESTPATH û no wonder the file didnÆt exist.)

Complaint #3: The D compile errors are obscure, occasionally incorrect, and happen in a multi-pass manner.

ItÆs probably a good thing that the D compiler is a multi-pass thing. There are no header files and no predeclerations. However, because the compiler is multi-pass, it tends to barf on an early pass before moving on to a next pass. I think there are 3ish compile passes and 1 link pass. If your code is significantly messed up, you will bring your number of errors down past 1 several times only to see new sets of errors crop up. (It could also be that I just caused more errors by fixing one) The errors usually refer to the line that failed, but not always. Factoring out code and block-commenting code seemed to be required. I had a problem with using an anonymous enum which reported the error on the declaration line, not on the usage line.

Complaint #4: The D run time errors are obscure.

When debugging in MSVC++, you get the line, call stack and all other information you could want about the failing code. When the program crashes, it would be nice to get a line number, a function name, or the option to debug the problem. D just dies to the command line with an error message and no context. The same problem applies to the asserts. MSVC++ asserts are continuable, possibly an implementation failure, since asserts should be fixed. It would be nice to be able to continue past asserts to get a comprehensive list of failures, but I recognize the point.

Being able to toss strings and arrays of objects around as parameters and return values without worrying about the memory leaks is really nice. In C++ you must always be aware of whose responsibility an object is. D is really nice about allowing you to pass references (low overhead on function calls) around without worrying about later releasing the objects or performing expensive copies.

DERELICT Game Library
After finishing my directory diff tool and deciding that the HTML source code reformatter was too boring to work on; I returned to trying to get some kind of a game working in Digital MarsÆ D programming language.

I downloaded Derelict, the everything-related-to-games-or-graphics linkage library for D. It takes good advantage of DÆs ability to statically and dynamically link to C without hassle. I liked Derelict. Add one additional initialization function call and the C code for your OpenGL or SDL program is very nearly ready to run.

Derelict needed Build / BUD in order to run. (BUD is the name of the tool formerly known as Build. BUD parses your source files as needed and acts as an intelligent MAKE. YouÆve already specified in your source which imports you need. BUD uses this information to compile and link your executable.). (Download this, then rename the junk file that downloaded as ôbud.exeö. The download link was broken for me.) After using BUD to get Derelict compiling, I discovered I like it better then dmd for my compiles. No make files. The naming scheme of your files appears to be sensitive, but you can build a single file and all dependencies will be dragged in.

One of the tutorials I read recommended breaking DerelictÆs zip file layout by searching for every subdirectory named ôderelictö and then dropping those files into DMDÆs SRC directory. This worked very well.

I grabbed one of NeheÆs articles (Try 6 and 15, I forget which I used) as a base, started with the OpenGL ôflying a little plane around the screen with the keyboardö and built upwards to a Asteroids / Bosconian type game.

I took that basic file and started adding layers of functionality. I mildly kept in mind how I would be using my code in the future, but mostly I just kept a list beside my keyboard of the features I wanted to add, and then refactored the code as I went.

I havenÆt been a fan of C# and JavaÆs one file layout because it seemed cluttered to me. A header gives you a nice concise list of what functions and structures are available, leaving the source file to do actual work in. Using BUD made splitting D source files very easy, since once the files marked what imports they needed, the compile just worked. D has nice class / object oriented support, but doesnÆt mandate it religiously. I find it easier to refactor stray functions out into new files than to break an existing class in half to make navigating the files easier.

If I had worked much longer with my little toy game, I would have broken the render and keyboard process functions in half. I had code for both gameplay and the main menu in the same functions and that looked ugly. I would probably add a function pointer ôprocess modeö system to handle which render and process was currently executing. After adding many enemies and weapons, I was starting to get irritated at the duplicated code in the ôbehaviorsö file. Most of that code could be converted to data and placed in an external script or in an .INI type definition file.

Various Notes on D

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.
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.
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:



Note: GameDev.net moderates article comments.