www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Using the C preprocessor with D code

reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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
Apr 05
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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.
Apr 05
next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Wed, Apr 05, 2017 at 06:21:48PM -0700, Walter Bright via Digitalmars-d 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.
Well, yes, but since D syntax is sufficiently close to C, the difference is immaterial. :-) Mostly, anyway. I can see the preprocessor getting confused by D-specific syntax like token strings or `` literals, but for the most part if the purpose is to expand C macros into D code snippets, this mostly shouldn't matter. T -- Не дорог подарок, дорога любовь.
Apr 05
prev sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
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.
Apr 06