Thread overview
Using the C preprocessor with D code
Apr 05, 2017
H. S. Teoh
Apr 06, 2017
Walter Bright
Apr 06, 2017
Patrick Schluter
April 05, 2017
D doesn't have a preprocessor, and for good reason, as it can allow all sorts of hard-to-find bugs and other issues that make code hard to maintain / understand.

However, some time ago I found an occasion where it was useful to run D code through a C preprocessor before handing it to the D compiler.  This is in the context of a wrapper I wrote around libfreetype for one of my projects.  It was easy enough to write declarations for Freetype functions that I needed (I didn't intend for the wrapper to be *complete*; just good enough for what I need), but when it came to handling Freetype errors, I didn't want to manually type in error definitions (which may change upon upgrading libfreetype).

Fortunately, Freetype itself comes with a flexible error-handling module in the form of the header file fterrdef.h, in which errors are defined as macros of the form FT_ERRORDEF_(name,code,msg) and FT_NOERRORDEC_(name,code,msg). By suitably defining these two macros and #include'ing the file, the user can generate useful things like tables of error messages, switch statement blocks for handling translating error codes, etc..

Of course, that's in the realm of C code, but since the C preprocessor actually doesn't care what language it's processing (all it really cares about is the #-directives, and on the side stripping C-style comments), it's actually possible to do this *directly from D code*. So here's what I did:

------------------------------------snip-----------------------------------
	module font.freetype_errors;

	alias FT_Error = int;

	/* This sets up the macros for extracting the error definitions */
	#define FT_ERRORDEF_(name,code,msg) \
	    enum FT_Err_ ## name = code;

	#define FT_NOERRORDEF_(name,code,msg) \
	    enum FT_Err_ ## name = code;

	/* This (evil!) magic does the actual emitting of the enum declaration */
	#include "freetype2/freetype/fterrdef.h"
	#undef FT_ERRORDEF_
	#undef FT_NOERRORDEF_

	string toString(FT_Error err)
	{
	    switch (err)
	    {
		/* This sets up the macros for extracting the error messages */
	#define FT_ERRORDEF_(name,code,msg) \
		case FT_Err_ ## name:   \
		    return msg;

	#define FT_NOERRORDEF_(name,code,msg) \
		case FT_Err_ ## name:   \
		    return msg;
	#include "freetype2/freetype/fterrdef.h"

		default:
		    import std.conv : to;
		    return "Freetype error " ~ to!string(cast(int)err);
	    }
	}
------------------------------------snip-----------------------------------

The D compiler, of course, can't compile this code, because it doesn't understand the #-directives. But I *can* preprocess it explicitly by running it through cpp and piping the output to an actual .d file that will be imported by the rest of my code.  The preprocessor does the work of actually expanding those error definitions into a D-style enum, as well as generate the body of a nice function for converting FT_Error into an error message defined by the libfreeetype sources.  Since this is automatically done, I don't even have to change the code when upgrading to a new version of libfreetype; any new error definitions will automatically be created for me. :-)

Who says you can't use a preprocessor with D code? ;-)


T

-- 
Let's not fight disease by killing the patient. -- Sean 'Shaleh' Perry
April 05, 2017
On 4/5/2017 1:50 PM, H. S. Teoh via Digitalmars-d wrote:
> Who says you can't use a preprocessor with D code? ;-)

There are some issues with it. The C preprocessor is defined to work on "preprocessor tokens", which are not quite the same thing as text.

April 06, 2017
On Thursday, 6 April 2017 at 01:21:48 UTC, Walter Bright wrote:
> On 4/5/2017 1:50 PM, H. S. Teoh via Digitalmars-d wrote:
>> Who says you can't use a preprocessor with D code? ;-)
>
> There are some issues with it. The C preprocessor is defined to work on "preprocessor tokens", which are not quite the same thing as text.

I did this kind of things in the '90s with 2 very different languages AutoLISP and Foxbase. Worked really well and allowed to avoid a lot of code duplication. These 2 languages were very deficient when speaking about modularity and global symbols of a project. There were really only 2 or 3 small things that couldn't be used with the macros. Syntactically D is much, much closer to C than Lisp or dBase language.