Why D _still_ needs the C preprocessor

David Medlock noone at nowhere.com
Wed May 9 17:56:58 PDT 2007


nobody at nowhere.nonet wrote:
> 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)
> 

If you don't mind using a local var:

Record rec;
Record[] select_records( rec, rec.type==LIB_HEADER );

where the function is defined:

Record[]  select_records( ref Record r, lazy bool clause )
{
   Record[] result;
   foreach(Record temp; items ) {
     r=temp; if ( clause() ) result ~= temp; }
   }
   return result;
}



-DavidM





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


More information about the Digitalmars-d-learn mailing list