www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why D _still_ needs the C preprocessor

reply nobody nowhere.nonet writes:
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 :
May 05 2007
next sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Hmm.... isn't this what the new mixins are for?

Example (just to fit in with your example, you'll have to adjust):

------
struct OMF_Record
{
	int record_type;
}

struct library
{
static:
	OMF_Record[] select_records (char[] clause) ()
	{
		OMF_Record[] results;

		OMF_Record rec;
		if (mixin(clause))
			results ~= rec;

		return results;
	}
}

int main()
{
	OMF_Record[] records = library.select_records!("rec.record_type == 1");

	return 0;
}
-----

That's what you want, right?  I mean, the ! isn't too bad is it?

-[Unknown]



 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.
 

May 05 2007
parent reply nobody nowhere.nonet writes:
Unknown W. Brackets <unknown simplemachines.org> spewed this unto the Network: 
 Hmm.... isn't this what the new mixins are for?
 
 Example (just to fit in with your example, you'll have to adjust):
 
 ------
 struct OMF_Record
 {
 	int record_type;
 }
 
 struct library
 {
 static:
 	OMF_Record[] select_records (char[] clause) ()
 	{
 		OMF_Record[] results;
 
 		OMF_Record rec;
 		if (mixin(clause))
 			results ~= rec;
 
 		return results;
 	}
 }
 
 int main()
 {
 	OMF_Record[] records = library.select_records!("rec.record_type == 1");
 
 	return 0;
 }
 -----
 
 That's what you want, right?  I mean, the ! isn't too bad is it?
 
 -[Unknown]

Well, that isn't too bad. But why does it work? I don't see the word "template" anywhere, but at this website: http://www.digitalmars.com/d/mixin.html It never says "mixin" without saying "template" first. I was under the impression that mixins only operated on templates. I've also never seen the ! away from the word "mixin". What does the "!" mean? And why does select_records()() get an extra set of parentheses now?
May 05 2007
next sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Haha, sorry.  It's a shortcut syntax for templates.  Here's a few 
clearer examples:

bool in_array (T) (T item, T[] array);
T max (T) (T[] array);

Essentially, it's like creating a template, with the parameters listed 
after the function first, with a function inside that has the second 
list of parameters and same return type.

I prefer them because it makes namespacing cleaner, usually.  Also, in 
the above examples, you can leave off the ! because it will guess based 
on parameters... meaning you could do:

int[] example;
int i = max(example);

In this case, it does mean that the clause cannot be dynamic, as it can 
in SQL (it must be known at compile time.)  For that, you'd have to 
introduce parameters of some sort, e.g.:

OMF_Record[] select_records (char[] clause) (box[] parameters...);

library.select_records!("rec.record_type == unbox!(int)(parameters[1])")(1);

I'm not sure if the above is exactly right off hand, but you get the 
idea.  That isn't quite as clean (although you could do more advanced 
parsing of the string at compile time to make it better.)

The ! is how you create a template.  In this case, I'm abusing the fact 
that you can leave parens off the function call.

Does that make more sense?

-[Unknown]


 Unknown W. Brackets <unknown simplemachines.org> spewed this unto the Network: 
 Hmm.... isn't this what the new mixins are for?

 Example (just to fit in with your example, you'll have to adjust):

 ------
 struct OMF_Record
 {
 	int record_type;
 }

 struct library
 {
 static:
 	OMF_Record[] select_records (char[] clause) ()
 	{
 		OMF_Record[] results;

 		OMF_Record rec;
 		if (mixin(clause))
 			results ~= rec;

 		return results;
 	}
 }

 int main()
 {
 	OMF_Record[] records = library.select_records!("rec.record_type == 1");

 	return 0;
 }
 -----

 That's what you want, right?  I mean, the ! isn't too bad is it?

 -[Unknown]

Well, that isn't too bad. But why does it work? I don't see the word "template" anywhere, but at this website: http://www.digitalmars.com/d/mixin.html It never says "mixin" without saying "template" first. I was under the impression that mixins only operated on templates. I've also never seen the ! away from the word "mixin". What does the "!" mean? And why does select_records()() get an extra set of parentheses now?

May 06 2007
next sibling parent nobody nowhere.nonet writes:
Unknown W. Brackets <unknown simplemachines.org> spewed this unto the Network: 
 Haha, sorry.  It's a shortcut syntax for templates.  Here's a few 
 clearer examples:
 
 bool in_array (T) (T item, T[] array);
 T max (T) (T[] array);
 
 Essentially, it's like creating a template, with the parameters listed 
 after the function first, with a function inside that has the second 
 list of parameters and same return type.
 
 I prefer them because it makes namespacing cleaner, usually.  Also, in 
 the above examples, you can leave off the ! because it will guess based 
 on parameters... meaning you could do:
 
 int[] example;
 int i = max(example);
 
 In this case, it does mean that the clause cannot be dynamic, as it can 
 in SQL (it must be known at compile time.)  For that, you'd have to 
 introduce parameters of some sort, e.g.:
 
 OMF_Record[] select_records (char[] clause) (box[] parameters...);
 
 library.select_records!("rec.record_type == unbox!(int)(parameters[1])")(1);
 
 I'm not sure if the above is exactly right off hand, but you get the 
 idea.  That isn't quite as clean (although you could do more advanced 
 parsing of the string at compile time to make it better.)
 
 The ! is how you create a template.  In this case, I'm abusing the fact 
 that you can leave parens off the function call.
 
 Does that make more sense?
 

It makes lots of sense now. Thanks.
May 06 2007
prev sibling parent Georg Wrede <georg nospam.org> writes:
Scary!

But probably just because

   - it's puts one in awe with it's power, right up front
   - it offer amazing economy of expression for a compiled C-family
     language
   - the documentation is scarce and scattered and obviously not
     written in an educational spirit :-(

It shouldn't be scary because it's _not_

   - intractable
   - rocket science

and it doesn't require "inhumane mental acrobatics", like the "hard-core 
template jujutsu" that's only accessible to the few of us. (That's not 
to say Walter should remove it!!)


Now, it might do D a lot of good if some of you guys got together and 
wrote one to two web pages, explaining things like

   - using templates with IFTI
   - combining delegates with both
   - maybe some other things related to this

If this gets well written (as in good textbook style) with good 
examples, then I believe most of us would happily end up using this 
stuff in regular code.

georg


BCS wrote:
 Reply to Justin,

 nobody nowhere.nonet Wrote:

 It's not as sugary sweet as you may want, but you can leave off the
 'delegate' keyword.

 OMF_Record[] results = library.select_records((OMF_Record rec) {
 return (rec.record_type == LIB_HEADER);});

auto results = lib.select_records((rec){return (rec.record_type == LIB_HEADER);}); as long as there is only one function that take a delegate that takes one argument, the type of the argument could be inferred.

Unknown W. Brackets wrote:
 Haha, sorry.  It's a shortcut syntax for templates.  Here's a few 
 clearer examples:
 
 bool in_array (T) (T item, T[] array);
 T max (T) (T[] array);
 
 Essentially, it's like creating a template, with the parameters listed 
 after the function first, with a function inside that has the second 
 list of parameters and same return type.
 
 I prefer them because it makes namespacing cleaner, usually.  Also, in 
 the above examples, you can leave off the ! because it will guess based 
 on parameters... meaning you could do:
 
 int[] example;
 int i = max(example);
 
 In this case, it does mean that the clause cannot be dynamic, as it can 
 in SQL (it must be known at compile time.)  For that, you'd have to 
 introduce parameters of some sort, e.g.:
 
 OMF_Record[] select_records (char[] clause) (box[] parameters...);
 
 library.select_records!("rec.record_type == 
 unbox!(int)(parameters[1])")(1);
 
 I'm not sure if the above is exactly right off hand, but you get the 
 idea.  That isn't quite as clean (although you could do more advanced 
 parsing of the string at compile time to make it better.)
 
 The ! is how you create a template.  In this case, I'm abusing the fact 
 that you can leave parens off the function call.
 
 Does that make more sense?
 
 -[Unknown]
 
 
 Unknown W. Brackets <unknown simplemachines.org> spewed this unto the 
 Network:

 Hmm.... isn't this what the new mixins are for?

 Example (just to fit in with your example, you'll have to adjust):

 ------
 struct OMF_Record
 {
     int record_type;
 }

 struct library
 {
 static:
     OMF_Record[] select_records (char[] clause) ()
     {
         OMF_Record[] results;

         OMF_Record rec;
         if (mixin(clause))
             results ~= rec;

         return results;
     }
 }

 int main()
 {
     OMF_Record[] records = library.select_records!("rec.record_type 
 == 1");

     return 0;
 }
 -----

 That's what you want, right?  I mean, the ! isn't too bad is it?

 -[Unknown]

Well, that isn't too bad. But why does it work? I don't see the word "template" anywhere, but at this website: http://www.digitalmars.com/d/mixin.html It never says "mixin" without saying "template" first. I was under the impression that mixins only operated on templates. I've also never seen the ! away from the word "mixin". What does the "!" mean? And why does select_records()() get an extra set of parentheses now?


May 08 2007
prev sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
nobody nowhere.nonet wrote:
 Unknown W. Brackets <unknown simplemachines.org> spewed this unto the Network: 
 Hmm.... isn't this what the new mixins are for?

 Example (just to fit in with your example, you'll have to adjust):

 ------
 struct OMF_Record
 {
 	int record_type;
 }

 struct library
 {
 static:
 	OMF_Record[] select_records (char[] clause) ()
 	{
 		OMF_Record[] results;

 		OMF_Record rec;
 		if (mixin(clause))
 			results ~= rec;

 		return results;
 	}
 }

 int main()
 {
 	OMF_Record[] records = library.select_records!("rec.record_type == 1");

 	return 0;
 }
 -----

 That's what you want, right?  I mean, the ! isn't too bad is it?

 -[Unknown]

Well, that isn't too bad. But why does it work? I don't see the word "template" anywhere, but at this website: http://www.digitalmars.com/d/mixin.html It never says "mixin" without saying "template" first. I was under the impression that mixins only operated on templates.

D has two distinct kinds of mixins: template mixins and the newer string literal mixins. The keyword index can help disambiguate them: http://www.prowiki.org/wiki4d/wiki.cgi?LanguageSpecification/KeywordIndex The spec very deliberately uses "template mixin" to unambiguously refer to one particular kind of mixin. The above code uses the other kind.
 I've also never seen the ! away from the word "mixin". What does the
 "!" mean?
 
 And why does select_records()() get an extra set of parentheses now?

The ! denotes a template instantiation. In the above, select_records is a function template. It has one template argument (char[] clause) and no function arguments. We can elide the trailing () when calling it because of D's property syntax. The "clause" argument must be a template argument, since string mixins can only operate on compile-time strings. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
May 06 2007
prev sibling next sibling parent reply Justin Scott <jrs7561 louisiana.edu> writes:
nobody nowhere.nonet Wrote:

 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's not as sugary sweet as you may want, but you can leave off the 'delegate' keyword. OMF_Record[] results = library.select_records((OMF_Record rec) { return (rec.record_type == LIB_HEADER);});
May 05 2007
parent reply BCS <ao pathlink.com> writes:
Reply to Justin,

 nobody nowhere.nonet Wrote:
 
 It's not as sugary sweet as you may want, but you can leave off the
 'delegate' keyword.
 
 OMF_Record[] results = library.select_records((OMF_Record rec) {
 return (rec.record_type == LIB_HEADER);});
 

auto results = lib.select_records((rec){return (rec.record_type == LIB_HEADER);}); as long as there is only one function that take a delegate that takes one argument, the type of the argument could be inferred.
May 06 2007
parent nobody nowhere.nonet writes:
BCS <ao pathlink.com> spewed this unto the Network: 
 Reply to Justin,
 
 nobody nowhere.nonet Wrote:
 
 It's not as sugary sweet as you may want, but you can leave off the
 'delegate' keyword.
 
 OMF_Record[] results = library.select_records((OMF_Record rec) {
 return (rec.record_type == LIB_HEADER);});
 

auto results = lib.select_records((rec){return (rec.record_type == LIB_HEADER);}); as long as there is only one function that take a delegate that takes one argument, the type of the argument could be inferred.

It would _seem_ that DMD should be able to infer the argument type, and also the number of arguments (and their names) from the prototype of select_records. That was the assumption I was making on my first attempt to write that function. Instead, DMD tries to figure out the type of the delegate in total isolation. It _ought_ to be possible to leave the (rec) out completely. I ended up using the mixin keyword to call select_records, which requires select_records to be a template, and that the query references a variable within the scope of select_records. Not the ideal solution, but not worse than other ideas, such as using lazy evaluation and an extra argument. As this thread was originally intended to argue that D still needs the C preprocessor (or more language features to prevent its resurgence), I have returned with another example, this time from code that is up for download on dsource.org (which includes its own preprocessor). The language feature that would eliminate the need for a preprocessor in this instance would be the ability to define null "keywords". Null keywords would be words that the D compiler simply ignores. They would replace preprocessor usages such as this, commonly found in C programs: #ifndef HAS_EXPORT #define export /* This D keyword is not recognized on Linux */ #endif Null keywords have to be legal anywhere that any keyword _might_ be legal, now or in the future, in DMD or any other implementation of D. The syntax for null keywords might look like this: /* Defines 'export' as a null keyword on Linux. * Causes a compilation error on Windows since * export is already a recognized keyword: */ nullword export; Here's why: I downloaded DAllegro, which implements a D binding to a game-programming library that seems to be popular among C programmers. DAllegro implements its own text preprocessor as a shell script, to account for the fact that the Linux version of DMD does not accept the "export" keyword. The shell script goes through the source and deletes every occurence of the word "export" so it'll compile. The readme.txt instructs the user to use the preprocessor only if compiling on Unix. This was apparently considered better than writing two "version" blocks every time the "export" keyword was used. A mixin template could provide another clunky solution to this problem, but there is no solution as elegant as simply being able to use the "export" keyword on Windows and define it to nothing on Linux. -- All your .signature are belong to us. Take off every 'sig' for great justice.
May 19 2007
prev sibling parent David Medlock <noone nowhere.com> writes:
nobody 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.
 

May 09 2007