Why D _still_ needs the C preprocessor
nobody at nowhere.nonet
nobody at nowhere.nonet
Sat May 5 19:25:58 PDT 2007
I am currently working on a program to manipulate OMF import libraries.
It turns out that OMF files consist of a series of records, and as
a result, a class that handles OMF files sort of resembles a database.
I decided to use one of D's best features, delegates, to add a member
whose function is analogous to SQL's "SELECT" statement. Impossible
implement in C and C++, it is declared as follows:
OMF_Record[] select_records(bool delegate(OMF_Record rec) where_clause) ;
I thought I could call this method as follows:
OMF_Record[] results = library.select_records({ return (rec.type == LIB_HEADER); });
It looks something like an SQL query, and it should do something similar as
well.
But it turns out that a delegate literal's type defaults to a delegate
that takes no arguments and returns an int. As a result, the above statement
does not compile, for two reasons: Wrong argument type, and 'rec' isn't
defined in that scope.
Since D does not implicitly give the delegate the correct type,
it takes a ton of keywords to make the delegate have the desired
type. The result is this:
OMF_Record[] results = library.select_records(delegate(OMF_Record rec) { return (rec.record_type == LIB_HEADER);});
Hideous, no matter where you place the tabs and line breaks. It is now
several orders of magnitude more complicated than an SQL query, and
it duplicates information: The type, name, and number of arguments
that the delegate must have will be repeated every time select_records
is used.
Also, it can be noticed that D actually does infer the return type of the
delegate (using 'bool' in the above expression causes a compile error),
but it still can't infer the number of arguments or their types.
So I tried to use D's "alias" keyword to make some syntactic sugar:
alias delegate(OMF_Record rec) where_t;
This gives an error to the effect that a basic type is required where
the word "delegate" is. This compiles:
alias bool delegate(OMF_Record rec) where_t;
But the resulting definition of where_t cannot be put in front of the
delegate literal (it can be used in the declaration of the member function,
however).
I tried using the following mixin:
template selrec() {
const char[] selrec = "delegate(OMF_Record rec)";
}
But this produced an error, and the invocation of the mixin had
twice as many parentheses as the expression it was intended to
replace.
I finally tried to use D's "lazy evaluation" feature, but I didn't
really expect it to work because it uses delegates underneath it
all:
class OMF {
/* ... */
// Change the declaration of the member
// function... It just happens to be that the
// foreach loop inside this function contains
// a variable called 'rec' that it was passing
// to the delegate as its 'rec' argument.
OMF_Record[] select_records(lazy bool where_clause) ;
/* ... */
}
/* ... */
// Now let's use it:
OMF_Record results = library.select_records(rec.type == LIB_HEADER);
But as it turns out (as I expected, which was why I tried this last),
D's "lazy" evaluation isn't as lazy as it would be if this was lazy
evaluation in an interpreted language. "rec" comes from the scope of the
calling function, not the scope of the foreach loop inside select_records
where the expression will be evaluated.
The only way I can get the result that I want is to subject the beautiful
D programmming language to the ugly C preprocessor. This enables me to
write this:
#define select_records(x) _select_records(delegate(OMF_Record rec) x)
...which would make it LOOK like the delegate implicitly has the type
of the argument (which was what I initially assumed, and what would
be better). This technique would bring with it all the perils of the
C preprocessor, and even threatens to wipe out some of the benefits
of D. If I released such a class as open-source software, its users
would have to subject _their_ programs to the CPP to be able to use
the class.
Fortunately (or not), I cannot abuse D in this way using DM's C
preprocessor, because the preprocessor seems to be built into
the C compiler somehow (as opposed to GCC, which has an external
CPP that can be called separately).
But D is still in its infancy. Over the years, future D programmers
will run into many duplicates of this problem, and they may eventually
cope with it by introducing an external C preprocessor to the mix.
--
Delete all files?
<Y>es, <S>ure, <A>bsolutely, <W>hy not :
More information about the Digitalmars-d-learn
mailing list