www.digitalmars.com         C & C++   DMDScript  

D - D and real Macros

reply Roland <rv ronetech.com> writes:
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

DMACROS

There is still a domain not already specified in D is replacement for
macros / Templates.
I think D cannot afford not to study MASM style of macros (oops i mean
ABEL HDL style of course).

Questions are:

1- is it usefull / powerfull ?

no hesitation: the answer is yes.
be sure it can do the job, and much more you think.

2- is it readable / easy to maintain ?

Those questions are one of the main reason why a brainstorm is
necessary.
My opinion can't count as I fell down the masm macro pot when i was a
baby.
You, the poors who never felt the power of what _real_ macros are, i
understand it could be a little dificult
at the beginning. but it worth the effort.
Below an example of a vector implementation, any type, any dimension,
using masm/abel style of macros.
To compare, please create the same in C++ using templates and send me it
<>
It will be double the size, and, at least for me, not more readable

3- is it easy to implement/parse ?

implementation:

A while ago, Walter wrote:

  Yeah, the Abel macro language was far more advanced than the C one

  simple to implement, too <g>).

Ok, so lets do the hard work and let Walter do the easy part <> Just i hope it can be done in only one compiler pass, else i'm sure he will hate me for the rest of his life. parsing: i'm not competent on that. Just i know compilers are not natural langage traducers and we must avoid context dependent syntax and ambiguities. I did my best but my version is probably not parsable at all. 4- is it easy to debug ? expanded macro must be easily displayed to the user. depend on the IDE i guess. not a good point here. lets see later. ///================================================================================= Example of pseudo-code using masm style macro. Not C++, not D. sorry: inspired by C++. The goal is to automaticaly create vector classes of any type, any dimension. - each vector of a type and of dimension n should inherit from the vector of the same type and dimension n-1, - vector functions need to be fast, - must be castable to an array of the vector dimension size: no other data. IMPORTANT: dmacros.h is attached with this post. read it with the idde: it will be more comfortable you can NOT evaluate readeabily of what folows without doing so ///--------------------------------------------------------------------------------- some basis: - i start all macro name with ' ' charactere. i think there must be a way to global search macros. - symbol declaration: #DEFINE, #DEFINE= #DEFINE= evaluates the expression to a number before assigning it to the symbol - the #LOCAL directive creates a unique name for use in macro. the name is defined only inside the macro. no need to undef it symbols names beginning with ?? are valid if they are names defined with #LOCAL. it help identifie temporary symbols - literal text operator: ^^literal text^^ - expression operator: %%expression: evaluates the expression to a number.(must evaluate to a number) - comment end of line: /// - comment: /** and **/ - #: concatenation of names. - args are substituted as soon as possible ///--------------------------------------------------------------------------------- ///tools #MACRO mkargline dim,arg,sep ///create a line ///for example: mkargline 3,p,= replace mkargline with its expanded value: p0=p1=p2 #LOCAL ??i #DEFINE mkargline arg#0 #FOR ??i = 1, ??i < %%dim, ??i++ ///I think you guess what #FOR/#ENDM macro-directive do #DEFINE mkargline mkargline#sep#arg#??i #ENDM #ENDM #MACRO opargval dim,arg,op,val ///op a value to eache arg ///for example opargval 3,coord,=,NAN expand: /// coord0 = NAN; /// coord1 = NAN; /// coord2 = NAN #LOCAL ??i #FOR ??i = 0, ??i < %%dim, ??i++ arg#??i op val; #ENDM #ENDM #MACRO opargarg dim,left_arg,op,right_arg,close_right_arg ///op right_arg to eache left_arg ///for example opargarg 3,coord,=,parray[,] expand: /// coord0 = parray[0]; /// coord1 = parray[1]; /// coord2 = parray[2]; #LOCAL ??i #FOR ??i = 0, ??i < %%dim, ??i++ left_arg#??i op right_arg#??i#close_right_arg; #ENDM #ENDM #MACRO mkcmpop dim,type,op /** example: mkcmpop 3,int,== expand like: inline int operator==(const Vector_3_int& a, const Vector_3_int& b) { return ( ( a.coord0==b.coord0 ) && ( a.coord1==b.coord1 ) && ( a.coord2==b.coord2 ) ); } i assume its faster than for (i=0; i<dim; i++) { if (a.coord[i]!=b.coord[i] break; } return (i>=dim); or something like that. **/ #LOCAL ??vect,??line,??i #DEFINE ??vect Vector_#%%dim#_#type inline int operator#op(const ??vect& a, const ??vect& b) { #DEFINE ??line (a.coord0#op#b.coord0) #FOR ??i = 1, ??i < %%dim, ??i++ #DEFINE ??line ??line && ( a.coord#??i#op#b.coord#??i ) #ENDM return ( ??line ); } #ENDM #MACRO mkopeq1 dim,type,op /** example: mkopeq1 3,int,+= expand like: inline Vector_3_int& operator+=(Vector_3_int& a, const int b) { a.coord0+=b; a.coord1+=b; a.coord2+=b; return a; } **/ #LOCAL ??vect #DEFINE ??vect Vector_#%%dim#_#type inline ??vect& operator#op(??vect& a, const type b) { opargval %%dim,a.coord,op,b return a; } #ENDM #MACRO mkopeq2 dim,type,op /** example: mkopeq2 3,int,+= expand like: inline Vector_3_int& operator+=(Vector_3_int& a, const Vector_3_int& b) { a.coord0+=b.coord0; a.coord1+=b.coord1; a.coord2+=b.coord2; return a; } **/ #LOCAL ??vect #DEFINE ??vect Vector_#%%dim#_#type inline ??vect& operator#op(??vect& a, const ??vect& b) { opargarg %%dim,a.coord,op,b.coord return a; } #ENDM #MACRO mkop1 dim,type,op /** example: mkop1 3,int,+ expand like: inline Vector_3_int operator+(const Vector_3_int& a, const int b) { Vector_3_int temp; temp.coord0 = a.coord0 + b; temp.coord1 = a.coord1 + b; temp.coord2 = a.coord2 + b; return temp; } **/ #LOCAL ??vect,??i #DEFINE ??vect Vector_#%%dim#_#type inline ??vect operator#op(const ??vect& a, const ??vect& b) { ??vect temp; #FOR ??i = 0, ??i < %%dim, ??i++ temp.coord#??i = a.coord#??i op b; #ENDM return temp; } #ENDM #MACRO mkop2 dim,type,op /** example: mkop2 3,int,+ expand like: inline Vector_3_int operator+(const Vector_3_int& a, const Vector_3_int& b) { Vector_3_int temp; temp.coord0 = a.coord0 + b.coord0; temp.coord1 = a.coord1 + b.coord1; temp.coord2 = a.coord2 + b.coord2; return temp; } **/ #LOCAL ??vect,??i #DEFINE ??vect Vector_#%%dim#_#type inline ??vect operator#op(const ??vect& a, const ??vect& b) { ??vect temp; #FOR ??i = 0, ??i < %%dim, ??i++ temp.coord#??i = a.coord#??i op b.coord#??i; #ENDM return temp; } #ENDM ///--------------------------------------------------------------------------------- ///now the main macro #MACRO DefVector dim,type #LOCAL ??i,??dim,??pdim, ??cvect,??pvect ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ///some defines #DEFINE= ??dim dim ///evaluate dim in case it is an expression. #DEFINE= ??pdim ??dim-1 ///same for previous dimension #IF (??dim < 2) ///discare vector one or negative(?) dimension #ERROR_MESSAGE " DefVector: dim must be >= 2" #EXITM ///exit #ENDIF #IFDEF Vector_#??dim#_#type ///vector already defined: exit #EXITM #ENDIF #DEFINE ??cvect Vector_#??dim#_#type ///??cvect is easier to write and read than Vector_#??dim#_#type #DEFINE ??pvect Vector_#??pdim#_#type ///??pvect is easier to write and read than Vector_#??pdim#_#type ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ///now create the struct #IF (??dim == 2) struct ??cvect { type coord0; //vector 2 dimension: create the 2 axis type coord1; #ELSE DefVector ??pdim,type ///create vector dimension dim-1, dim-2..2 if they don't already exists struct ??cvect: ??pvect { ///dim vector inherits from vector dim-1 type coord#??pdim; ///the last dimension: coord<dim-1> #ENDIF ///#IF (??dim == 2) //constructors ??cvect() { ///default constructor: nop for int vectors, NAN for float vectors #IF type.isfloat opargval ??dim,coord,=,NAN #ENDIF } ??cvect( mkargline ??dim,const type p,^^,^^ ) { //space before ')': can't be passed as an argument to mkargline ///example: for Vector_3_int, create constructor Vector_3_int(int p0, int p1, int p2); opargarg ??dim,coord,=,p } ??cvect(const ??pvect& pp, const type lp): ??pvect(pp), coord#??pdim(lp) { ///example: for Vector_3_double, create constructor Vector_3_double(Vector_2_double& pp, double lp); } ??cvect(const type* parray) { ///example: for Vector_3_double, create constructor Vector_3_double(double* parray) ///!! not safe: no check on parray size. opargarg ??dim,coord,=,parray[,] } ??cvect(const ??cvect& p) { ///copy constructor ///useless if you keep ??cvect(type* parray) constructor ///hope the compiler can choose this one before choosing ??cvect(type* parray) associated with ///type* operator below opargarg ??dim,coord,=,p.coord } #IF type.isinteger ??cvect(const double* parray) { ///conversion from a double array to the int vector ///!! not safe: no check on parray size. opargarg ??dim,coord,=,rndtonl(p,) } #ENDIF ///#IF type.isinteger #IF type.isfloat ??cvect(const int* parray) { ///conversion from a int array to the float vector ///!! not safe: no check on parray size. opargarg ??dim,coord,=,parray[,] } #ENDIF ///#IF type.isfloat ///operators operator type*() { ///hope would not create a lot of ambiguities. return &coord0; } #IFNDEF NDEBUG type& operator[](const int i) { ///if debug, use this one: check i ///else, use operator type* ///hope no ambiguity with operator type* assert(i>=0) && (i<??dim)); // <--- here return (&coord0)[i]; } #ENDIF ///#IFNDEF NDEBUG ///other methodes static int ^^dim^^() { //^^dim^^: here do NOT replace 'dim' with macro arg dim return ??dim; } }; ///end struct ??cvect ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ///now create operator functions: //int operator==(const ??cvect& a, const ??cvect& b); //int operator!=(const ??cvect& a, const ??cvect& b); //int operator>=(const ??cvect& a, const ??cvect& b); //int operator<=(const ??cvect& a, const ??cvect& b); //int operator>(const ??cvect& a, const ??cvect& b); //int operator<(const ??cvect& a, const ??cvect& b); #IRP ??op,(==,!=,>=,<=,>,<) ///#IRP/#ENDM macro-directive: for each operator in the list, put it in ??op and "call" mkcmpop mkcmpop ??dim,type,??op #ENDM //??cvect& operator-=(??cvect& a, const type b); //??cvect& operator+=(??cvect& a, const type b); //??cvect& operator*=(??cvect& a, const type b); //??cvect& operator/=(??cvect& a, const type b); #IRP ??op,(-=,+=,*=,/=) mkopeq1 ??dim,type,??op #ENDM //??cvect& operator-=(??cvect& a, const ??cvect& b); //??cvect& operator+=(??cvect& a, const ??cvect& b); //??cvect& operator*=(??cvect& a, const ??cvect& b); //??cvect& operator/=(??cvect& a, const ??cvect& b); #IRP ??op,(-=,+=,*=,/=) mkopeq2 ??dim,type,??op #ENDM #IF type.isinteger ??cvect& operator>>=(??cvect& a, const int b) { opargval ??dim,a.coord,>>=,b return a; } ??cvect& operator<<=(??cvect& a, const int b) { opargval ??dim,a.coord,<<=,b return a; } #ENDIF ///#IF type.isinteger ??cvect operator-(const ??cvect& a) { ??cvect temp; opargarg temp.coord,=-,a.coord return temp; } //??cvect operator-(const ??cvect& a, const type b); //??cvect operator+(const ??cvect& a, const type b); //??cvect operator*(const ??cvect& a, const type b); //??cvect operator/(const ??cvect& a, const type b); #IRP ??op,(-,+,*,/) mkop1 ??dim,type,??op #ENDM //??cvect operator-(const ??cvect& a, ??cvect& b); //??cvect operator+(const ??cvect& a, ??cvect& b); //??cvect operator*(const ??cvect& a, ??cvect& b); //??cvect operator/(const ??cvect& a, ??cvect& b); #IRP ??op,(-,+,*,/) mkop2 ??dim,type,??op #ENDM #IF type.isinteger ??cvect operator>>(const ??cvect& a, const int b) { ??ctype temp; opargarg ??dim,temp.coord,=,a.coord,>>b return temp; } ??cvect operator<<(const ??cvect& a, const int b) { ??ctype temp; opargarg ??dim,temp.coord,=,a.coord,<<b return temp; } #ENDIF ///#IF type.isinteger ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ///other function: ??cvect abs(const ??cvect& a) { ??ctype temp; #IF type.isinteger opargarg ??dim,temp.coord,=,abs(a.coord,) #ENDIF #IF type.isfloat opargarg ??dim,temp.coord,=,fabs(a.coord,) #ENDIF return temp; } #IF type.isfloat ??cvect ceil(const ??cvect& a) { ??ctype temp; opargarg ??dim,temp.coord,=,ceil(a.coord,) return temp; } ??cvect floor(const ??cvect& a) { ??ctype temp; opargarg ??dim,temp.coord,=,floor(a.coord,) return temp; } ??cvect round(const ??cvect& a) { ??ctype temp; opargarg ??dim,temp.coord,=,round(a.coord,) return temp; } #ENDIF ///#IF type.isfloat . . exercice: create the vector matrix, create function for scalar product, vectorial product, etc..etc.. . . #ENDM ///#MACRO DefVector dim,type ///--------------------------------------------------------------------------------- Thank you for your attention roland
Apr 16 2002
next sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
Powerful.  Fast.  And Oh My God so utterly incomprehensible to someone who
didn't write the code.  Nightmarish to maintain or debug.

I for one am glad Walter doesn't like macros.  I'd rather have a compiler
that had a really good optimizer than have a language with macro support.
And as everyone keeps bringing up, you can always use an external macro
processing tool to preprocess your source files.

Sean


"Roland" <rv ronetech.com> wrote in message
news:3CBC7C3D.C9ADB732 ronetech.com...
 DMACROS

 There is still a domain not already specified in D is replacement for
 macros / Templates.
 I think D cannot afford not to study MASM style of macros (oops i mean
 ABEL HDL style of course).

 Questions are:

 1- is it usefull / powerfull ?

 no hesitation: the answer is yes.
 be sure it can do the job, and much more you think.

 2- is it readable / easy to maintain ?

 Those questions are one of the main reason why a brainstorm is
 necessary.
 My opinion can't count as I fell down the masm macro pot when i was a
 baby.
 You, the poors who never felt the power of what _real_ macros are, i
 understand it could be a little dificult
 at the beginning. but it worth the effort.
 Below an example of a vector implementation, any type, any dimension,
 using masm/abel style of macros.
 To compare, please create the same in C++ using templates and send me it
 <>
 It will be double the size, and, at least for me, not more readable

 3- is it easy to implement/parse ?

 implementation:

 A while ago, Walter wrote:

  Yeah, the Abel macro language was far more advanced than the C one

  simple to implement, too <g>).

Ok, so lets do the hard work and let Walter do the easy part <> Just i hope it can be done in only one compiler pass, else i'm sure he will hate me for the rest of his life. parsing: i'm not competent on that. Just i know compilers are not natural langage traducers and we must avoid context dependent syntax and ambiguities. I did my best but my version is probably not parsable at all. 4- is it easy to debug ? expanded macro must be easily displayed to the user. depend on the IDE i guess. not a good point here. lets see later.

========
 Example of pseudo-code using masm style macro.

<SNIP unreadable macro example code>
Apr 17 2002
next sibling parent "Richard Krehbiel" <rich kastle.com> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:a9jhho$2vjn$1 digitaldaemon.com...
 Powerful.  Fast.  And Oh My God so utterly incomprehensible to someone who
 didn't write the code.  Nightmarish to maintain or debug.

 I for one am glad Walter doesn't like macros.  I'd rather have a compiler
 that had a really good optimizer than have a language with macro support.
 And as everyone keeps bringing up, you can always use an external macro
 processing tool to preprocess your source files.

I still believe D would benefit from a real macro language. I have a bullet list of features: 1. Scriptable; macros that you can really *program*, with control flow structures like while, if/else, switch, etc. For an example, consider embedded PHP in an HTML web page. 2. Similarity to the language being compiled. A D macro script should have syntax and semantics as similar as possible to a regular D function. 3. Access to the symbol tables of the program being compiled. That means that a macro can query variable types, enumerate the members of structures and classes, etc. In CPP, you can't say "#if sizeof(var) == 4" because the stupid preprocessor doesn't have a clue about program variables. Java and C# programs have access to this info at run time, by bloating it's executables with loads of object type information; I mean to do it at compile time. 4. The ability for a macro to escape source language syntax constraints. I'd like to code a macro to be used thus: "print(a, b, c:16);" Notice the print width specifier on c. (Also notice that the macro senses the types of the arguments.) Or this: "EXEC SQL INSERT INTO TABLE1 ( COL1 ) VALUES :var );" The macro is EXEC, and it parsed and dealt with an SQL statement, including a binding to a D language variable. I got this crazy idea in my head that I could write the preprocessor that can do this - and actually, it would mostly be a D language interpreter with some extra features. I started working on the YACC grammar for D (I'm better with YACC than open-coded parsers like Walter's doing), but it's slow going, partly because the grammer info on the web page is incomplete, but mostly because my life affords me precious little time. I wish somebody could afford to pay me to do this full time... -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Apr 17 2002
prev sibling parent reply Roland <rv ronetech.com> writes:
"Sean L. Palmer" a écrit :

 And as everyone keeps bringing up, you can always use an external macro
 processing tool to preprocess your source files.

 Sean

you are probably right
 <SNIP unreadable macro example code>

really so horrible ? hope you read the .H with an idde that highlight the preprocessor directives. roland
Apr 17 2002
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
All those ?? and # are really ugly as well... surely there would be a way to
do it without so many wierd symbols everywhere.

Listen, I can figure out your macros, I've written many assembler macros in
my time.  And I kinda like the idea of automatically-generated code.  But
your example is basically unreadable because of all the symbols.  I'm
actually more in favor of something like forced-inline functions combined
with generics, and language features that enable the generics to deal with
unknown types nicely, such as

bool Foo(type T : IComparable tparm, type uparm) // whatever you pass for T
must be derived from IComparable; uparm can be anything
{
  typealias U = typeof(uparm);
  U ucopy = uparm; // declare new variable same type as uparm
  T tcopy = tparm;   // tparm's type was named in the declaration

  // the following checks compile time constant properties of the type to
determine what kind of code to generate
  // blocks controlled by constant compile time false are never evaluated
sematically thus won't cause compile errors.
  printf("U is of type %s",U.name);
  if (U.isstruct)
  {
    printf("U is a struct\n");
    for (int i=0; i<U.numfields; ++i)
      printf("  U field #%d named %s is type %s\n", i, U.fields[i].name,
U.fields[i].type.name);
  }
  else if (U.isclass)
  {
    printf("U is a class derived from %s\n",U.type.ancestor);
  }
  else if (U.isarray)
  {
     printf("U is an array %d elements\n",uparm.size);
    for (int i=0; i<U.numfields; ++i)
      printf("  U element #%d is %s\n", i, uparm[i].tostring);
  }
  else if (U.isenum) printf("U is an enum %s\n",uparm.tostring);
  else if (U.isintegral) printf("U is a integral %l\n",long(uparm));
  else if (U.isreal) printf("U is a real %f\n",extended(uparm));
  else printf("dunno how to deal with type U\n");

  assert(T.derivesfrom(IComparable)); // just to give a meaningful error
message; without the assert it still would fail to compile

  return tcopy.CompareWith(ucopy); // type T must provide this function
overloaded for type U or a compile error will result during expansion.
}

Since most of the stuff in there is known at compile time, quite a bit of
compile time optimization is possible.  And I'm sure people could come up
with more stuff that would enable even cooler tricks.

Sean

"Roland" <rv ronetech.com> wrote in message
news:3CBDBA40.B61E6445 ronetech.com...
 <SNIP unreadable macro example code>

really so horrible ? hope you read the .H with an idde that highlight the preprocessor

 roland

Apr 17 2002
parent reply Roland <rv ronetech.com> writes:
"Sean L. Palmer" a écrit :

 All those ?? and # are really ugly as well... surely there would be a way to
 do it without so many wierd symbols everywhere.

ok, I could have choosen a more appealing syntax. one problem is that i like a separate syntax for directive and statement. I'm just understanding it is not necessary if you have a good optimizer. now i understand why in this example (C++ sorry): template <class T, int SIZE> class Toto { int afunc() { //you write if (SIZE==2) { //instead of #if (SIZE==2) { //as it always seems logical to me . . } } };
 Listen, I can figure out your macros, I've written many assembler macros in
 my time.  And I kinda like the idea of automatically-generated code.  But
 your example is basically unreadable because of all the symbols.  I'm
 actually more in favor of something like forced-inline functions combined
 with generics, and language features that enable the generics to deal with
 unknown types nicely, such as

 bool Foo(type T : IComparable tparm, type uparm) // whatever you pass for T
 must be derived from IComparable; uparm can be anything
 {
   typealias U = typeof(uparm);
   U ucopy = uparm; // declare new variable same type as uparm
   T tcopy = tparm;   // tparm's type was named in the declaration

   // the following checks compile time constant properties of the type to
 determine what kind of code to generate
   // blocks controlled by constant compile time false are never evaluated
 sematically thus won't cause compile errors.
   printf("U is of type %s",U.name);
   if (U.isstruct)
   {
     printf("U is a struct\n");
     for (int i=0; i<U.numfields; ++i)
       printf("  U field #%d named %s is type %s\n", i, U.fields[i].name,
 U.fields[i].type.name);
   }
   else if (U.isclass)
   {
     printf("U is a class derived from %s\n",U.type.ancestor);
   }
   else if (U.isarray)
   {
      printf("U is an array %d elements\n",uparm.size);
     for (int i=0; i<U.numfields; ++i)
       printf("  U element #%d is %s\n", i, uparm[i].tostring);
   }
   else if (U.isenum) printf("U is an enum %s\n",uparm.tostring);
   else if (U.isintegral) printf("U is a integral %l\n",long(uparm));
   else if (U.isreal) printf("U is a real %f\n",extended(uparm));
   else printf("dunno how to deal with type U\n");

   assert(T.derivesfrom(IComparable)); // just to give a meaningful error
 message; without the assert it still would fail to compile

   return tcopy.CompareWith(ucopy); // type T must provide this function
 overloaded for type U or a compile error will result during expansion.
 }

 Since most of the stuff in there is known at compile time, quite a bit of
 compile time optimization is possible.  And I'm sure people could come up
 with more stuff that would enable even cooler tricks.

 Sean

you got the idea. now is it interesting enough to search for a syntax ? for me yes roland
Apr 18 2002
parent reply "Marius S." <mars centras.lt> writes:
I don't like the idea of separate syntax for the macro language.
I think it would be more convenient to have similar syntax to D
and special statements indicating macros.
For example something like this:

macro void test(int arg)
{
   printf("a = %d;\n", arg);
}

what do you think?
you then could write
"macro a" or "macro{ a b }" and maybe "macro: a" (like "public")
just a thought... :)

Roland wrote:
 "Sean L. Palmer" a écrit :
 
 
All those ?? and # are really ugly as well... surely there would be a way to
do it without so many wierd symbols everywhere.

ok, I could have choosen a more appealing syntax. one problem is that i like a separate syntax for directive and statement. I'm just understanding it is not necessary if you have a good optimizer. now i understand why in this example (C++ sorry): template <class T, int SIZE> class Toto { int afunc() { //you write if (SIZE==2) { //instead of #if (SIZE==2) { //as it always seems logical to me . . } } };
Listen, I can figure out your macros, I've written many assembler macros in
my time.  And I kinda like the idea of automatically-generated code.  But
your example is basically unreadable because of all the symbols.  I'm
actually more in favor of something like forced-inline functions combined
with generics, and language features that enable the generics to deal with
unknown types nicely, such as

bool Foo(type T : IComparable tparm, type uparm) // whatever you pass for T
must be derived from IComparable; uparm can be anything
{
  typealias U = typeof(uparm);
  U ucopy = uparm; // declare new variable same type as uparm
  T tcopy = tparm;   // tparm's type was named in the declaration

  // the following checks compile time constant properties of the type to
determine what kind of code to generate
  // blocks controlled by constant compile time false are never evaluated
sematically thus won't cause compile errors.
  printf("U is of type %s",U.name);
  if (U.isstruct)
  {
    printf("U is a struct\n");
    for (int i=0; i<U.numfields; ++i)
      printf("  U field #%d named %s is type %s\n", i, U.fields[i].name,
U.fields[i].type.name);
  }
  else if (U.isclass)
  {
    printf("U is a class derived from %s\n",U.type.ancestor);
  }
  else if (U.isarray)
  {
     printf("U is an array %d elements\n",uparm.size);
    for (int i=0; i<U.numfields; ++i)
      printf("  U element #%d is %s\n", i, uparm[i].tostring);
  }
  else if (U.isenum) printf("U is an enum %s\n",uparm.tostring);
  else if (U.isintegral) printf("U is a integral %l\n",long(uparm));
  else if (U.isreal) printf("U is a real %f\n",extended(uparm));
  else printf("dunno how to deal with type U\n");

  assert(T.derivesfrom(IComparable)); // just to give a meaningful error
message; without the assert it still would fail to compile

  return tcopy.CompareWith(ucopy); // type T must provide this function
overloaded for type U or a compile error will result during expansion.
}

Since most of the stuff in there is known at compile time, quite a bit of
compile time optimization is possible.  And I'm sure people could come up
with more stuff that would enable even cooler tricks.

Sean

you got the idea. now is it interesting enough to search for a syntax ? for me yes roland

Apr 22 2002
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
So what, then, is the difference between a macro and an inlined function?

Sean

"Marius S." <mars centras.lt> wrote in message
news:3CC4A2FB.3000403 centras.lt...
 I don't like the idea of separate syntax for the macro language.
 I think it would be more convenient to have similar syntax to D
 and special statements indicating macros.
 For example something like this:

 macro void test(int arg)
 {
    printf("a = %d;\n", arg);
 }

 what do you think?
 you then could write
 "macro a" or "macro{ a b }" and maybe "macro: a" (like "public")
 just a thought... :)

 Roland wrote:

Apr 22 2002
next sibling parent reply "Marius S." <mars centras.lt> writes:
macro is compile-time function :)

Sean L. Palmer wrote:
 So what, then, is the difference between a macro and an inlined function?
 
 Sean
 
 "Marius S." <mars centras.lt> wrote in message
 news:3CC4A2FB.3000403 centras.lt...
 
I don't like the idea of separate syntax for the macro language.
I think it would be more convenient to have similar syntax to D
and special statements indicating macros.
For example something like this:

macro void test(int arg)
{
   printf("a = %d;\n", arg);
}

what do you think?
you then could write
"macro a" or "macro{ a b }" and maybe "macro: a" (like "public")
just a thought... :)


Apr 23 2002
parent reply "Richard Krehbiel" <rich kastle.com> writes:
"Marius S." <mars centras.lt> wrote in message
news:3CC51C12.8030902 centras.lt...
 macro is compile-time function :)

Preeee-cisely. :-) A macro is a function that is executed at compile time. It has the program source code as an input stream, and output it writes is input to the compiler. It intimate with the compiler's current context, so that it can discover variable and expression types, it can interrogate class and struct membership, and it can do, well, whatever else might be useful (read and write files; issue database queries; perform socket/Internet transactions).
 Sean L. Palmer wrote:
 So what, then, is the difference between a macro and an inlined


 Sean

 "Marius S." <mars centras.lt> wrote in message
 news:3CC4A2FB.3000403 centras.lt...

I don't like the idea of separate syntax for the macro language.
I think it would be more convenient to have similar syntax to D
and special statements indicating macros.
For example something like this:

macro void test(int arg)
{
   printf("a = %d;\n", arg);
}

what do you think?
you then could write
"macro a" or "macro{ a b }" and maybe "macro: a" (like "public")
just a thought... :)



-- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Apr 23 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Richard Krehbiel" <rich kastle.com> wrote in message
news:aa3jr9$2pka$1 digitaldaemon.com...
 A macro is a function that is executed at compile time.  It has the

 source code as an input stream, and output it writes is input to the
 compiler.  It intimate with the compiler's current context, so that it can
 discover variable and expression types, it can interrogate class and

 membership, and it can do, well, whatever else might be useful (read and
 write files; issue database queries; perform socket/Internet

Macros are great, but they make it impractical to separate the tokenizing step from the semantic processing.
Apr 23 2002
next sibling parent reply "Richard Krehbiel" <rich kastle.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:aa4svh$19ia$1 digitaldaemon.com...
 "Richard Krehbiel" <rich kastle.com> wrote in message
 news:aa3jr9$2pka$1 digitaldaemon.com...
 A macro is a function that is executed at compile time.  It has the

 source code as an input stream, and output it writes is input to the
 compiler.  It intimate with the compiler's current context, so that it


 discover variable and expression types, it can interrogate class and

 membership, and it can do, well, whatever else might be useful (read and
 write files; issue database queries; perform socket/Internet

Macros are great, but they make it impractical to separate the tokenizing step from the semantic processing.

Um... why is this important? Here's my understanding of compiler phases: Once upon a time, when 4K machines were being asked to compile FORTRAN, it was crucial to be able to separate lexical analysis from syntactic, syntactic from semantic, semantic from optimization, optimization from assembly, etc so that the individual phases could fit into core, and each phase would forward their results to the next phase in intermediate files. No longer. Today's compiler does everything but linking in one step, and everything fits in memory (and if not in real memory, then virtual). So what if it's not easy to make a token stream from your input source? I'm afraid I don't get it. -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Apr 24 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Richard Krehbiel wrote:

 Um... why is this important?

 Here's my understanding of compiler phases: Once upon a time, when 4K
 machines were being asked to compile FORTRAN, it was crucial to be able to
 separate lexical analysis from syntactic, syntactic from semantic, semantic
 from optimization, optimization from assembly, etc so that the individual
 phases could fit into core, and each phase would forward their results to
 the next phase in intermediate files.

 No longer.  Today's compiler does everything but linking in one step, and
 everything fits in memory (and if not in real memory, then virtual).

Maybe 15 years (?) ago, the first single-pass compiler (Turbo C?) came out, and the point of doing it all in one pass was to increase performance. However, any time that you tightly integrate two very different parts of the code (i.e. token parsing with semantic analysis, or semantic analysis with compilation), you make things more complex and harder to debug. That means that compilers are harder to write, harder to "prove correct," and therefore more likely to have subtle errors. Errors in compilers, or having a wide variety of compilers with differing levels of support for various features, is a Bad Thing. One of D's stated objectives is to make it easy to write a correct compiler that implements all of the features. By keeping the phases separate, this is much easier to do. Similarly, it allows people to write tools to view, debug, or perhaps even rearrange D code without having to parse all of the import files to find odd macros. Thus, you can bet that MY_THING *[]*foo; is the declaration of a variable called foo, and that MY_THING is a user type declared elsewhere - you don't have to parse for macros and find out that MY_THING is some weird alternate expansion. -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Apr 24 2002
parent reply "Richard Krehbiel" <rich kastle.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3CC6DFC0.F01424E6 deming-os.org...
 Richard Krehbiel wrote:

 Um... why is this important?

Similarly, it allows people to write tools to view, debug, or perhaps even rearrange D code without having to parse all of the import files to find

 macros.  Thus, you can bet that
     MY_THING *[]*foo;
 is the declaration of a variable called foo, and that MY_THING is a user

 declared elsewhere - you don't have to parse for macros and find out that
 MY_THING is some weird alternate expansion.

Oh, I think I get it. The simpler a language is to parse, the more IDE-able it is. I'm thinking of the Visual Studio IDE, where you type along: struct a { int z; double y; } var; var. ...and upon typing the "." the editor gives you a pop-up to choose among the struct members. The editor has interpreted the struct definition in real time. A package of fancy macros could certainly defeat that. Indeed, the simple macros of CPP defeat Visual Studio's editor quite handily. So a language with powerful macros would need a more powerful IDE. (And here was I, wondering what use is a 2.4GHz processor to a plain text editor - why, it's running a full compile behind every keystroke...) -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Apr 24 2002
parent reply Russell Borogove <kaleja estarcion.com> writes:
Richard Krehbiel wrote:
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3CC6DFC0.F01424E6 deming-os.org...
 
Richard Krehbiel wrote:


Um... why is this important?

[ ... ] Similarly, it allows people to write tools to view, debug, or perhaps even rearrange D code without having to parse all of the import files to find

odd
macros. [snip]

Oh, I think I get it. The simpler a language is to parse, the more IDE-able it is.

Among other things. Walter did say tools to "view, debug, or ... rearrange" code. This includes, say, putting it through a Perl script that looks for certain constructs and replaces them with other constructs. I've tried to do this in C++ and been stymied by its syntactical insanities, and usually wound up with "well, it works mostly, but I'll have to manually fix about 100 lines of code... or spend another day improving my script." -Russell B
Apr 25 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3CC8325C.8090607 estarcion.com...
 Among other things. Walter did say tools to "view, debug,
 or ... rearrange" code. This includes, say, putting it
 through a Perl script that looks for certain constructs
 and replaces them with other constructs. I've tried to
 do this in C++ and been stymied by its syntactical
 insanities, and usually wound up with "well, it works
 mostly, but I'll have to manually fix about 100 lines
 of code... or spend another day improving my script."

The existence of the preprocessor in C++ essentially prevents syntax directed editors, pretty printers, formatters, and various simple parsers (such as wizards) from working 100% reliably. You cannot know what anything is unless you know exactly what the compiler predefined symbols are, what the arguments to the compiler are, what the include path is, what all the include files are, etc. What those simple parsers do is make assumptions about the way most programmers use macros. That will work 98% of the time, but they cannot work 100% of the time. Struggling with that last 2% consumes endless time and frustration. With D, a simple parser can be made that will work reliably 100% of the time. The compiler even comes with the source to the lexer and parser you can use as a guide to how to do it right.
Apr 26 2002
parent Roland <rv ronetech.com> writes:
Walter a écrit :

 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3CC8325C.8090607 estarcion.com...
 Among other things. Walter did say tools to "view, debug,
 or ... rearrange" code. This includes, say, putting it
 through a Perl script that looks for certain constructs
 and replaces them with other constructs. I've tried to
 do this in C++ and been stymied by its syntactical
 insanities, and usually wound up with "well, it works
 mostly, but I'll have to manually fix about 100 lines
 of code... or spend another day improving my script."

The existence of the preprocessor in C++ essentially prevents syntax directed editors, pretty printers, formatters, and various simple parsers (such as wizards) from working 100% reliably. You cannot know what anything is unless you know exactly what the compiler predefined symbols are, what the arguments to the compiler are, what the include path is, what all the include files are, etc. What those simple parsers do is make assumptions about the way most programmers use macros. That will work 98% of the time, but they cannot work 100% of the time. Struggling with that last 2% consumes endless time and frustration. With D, a simple parser can be made that will work reliably 100% of the time. The compiler even comes with the source to the lexer and parser you can use as a guide to how to do it right.

It seems D is made for an external and optional macro preprocessor, and/or a idde that help the programmer in function parameter passing or struct field choice like in vba. If i understand well, you can't have both fonctionality at the same time. In the prévision of a future macro preprocessor, it's good to keep some keyword/keychar reserved like , #, $, "macro" or else. roland
Apr 26 2002
prev sibling parent reply Georg Wrede <georg iki.fi> writes:
Maybe we should consider the roles macros play separately?

As I see, preprocessor macros are used to

 - save typing 
 - compensate for language (or compiler) deficiecies
 - create new constructs or try new languages or syntaxes
 - facilitate making different versions of output
 - easy creation of debugging runtimes from same source
 - facilitate compiling in different environments


What else? Should we refactor this list so everyone first
agrees on it, and then examine the issues in separate
news-threads?

Save typing here includes faster writing, less errors,
and at times clearer code. Language deficiencies here
means also the need for gathering and ordering input 
from files (#includes). The other categories are
self explanatory.

D seems to already take care of the last three of
them. Formidably, I might add.

Suppose you regularly end up in a situation where 
writing a macro saves more time coding than writing 
the macro takes, can we take that prima facie meaning
that the language has deficiencies? (Unless, of course,
you are doing something where you should use another
language to begin with!)

Georg
Apr 26 2002
parent "Walter" <walter digitalmars.com> writes:
"Georg Wrede" <georg iki.fi> wrote in message
news:3CC91DE8.35E415AF iki.fi...
 Suppose you regularly end up in a situation where
 writing a macro saves more time coding than writing
 the macro takes, can we take that prima facie meaning
 that the language has deficiencies?

There will always be a place for text generation. Interestingly, I find the C preprocessor quite inadequate for generating text. Look at impcnvgen.c and idgen.c in the compiler source - both are programs that write source which is then compiled into the compiler. I think such things, however, are properly not part of the language. Use any text processing macro program to generate D source you like.
Apr 26 2002
prev sibling parent reply Roland <rv ronetech.com> writes:
"Sean L. Palmer" a écrit :

 So what, then, is the difference between a macro and an inlined function?

 Sean

 "Marius S." <mars centras.lt> wrote in message
 news:3CC4A2FB.3000403 centras.lt...
 I don't like the idea of separate syntax for the macro language.
 I think it would be more convenient to have similar syntax to D
 and special statements indicating macros.
 For example something like this:

 macro void test(int arg)
 {
    printf("a = %d;\n", arg);
 }

 what do you think?
 you then could write
 "macro a" or "macro{ a b }" and maybe "macro: a" (like "public")
 just a thought... :)

 Roland wrote:


right. to be usefull, arg must not be typed: macro void test(arg) { . . } so what is the difference with template function ? none exept that it could be parcical to have some more directives like symbole name concatenation, repeat statement directive, local symbole names, etc.. macro void opeq(dim,p,op,a) { int i; for (i=0; i<dim; i++) { //dim is a constant. compiler may unfold the loop p.coord#i op a; //# concatenate symbols. p.coord0 op a; p.coord1 op a... } } now what does it do ? example: void afunction(point3d& p; const int a) { . . opeq(3,p,-=,a); . . } expand like void afunction(point3d& p; const int a) { . . p.coord0 -= a; p.coord1 -= a; p.coord2 -= a; . . } ??????? personaly i made my mind: complex macros should be processed externally from the compiler. just i have to find a good preprocessor for that. any suggestions ? roland
Apr 23 2002
parent "Marius S." <mars centras.lt> writes:
Roland wrote:
 
 "Sean L. Palmer" a écrit :
 
 
So what, then, is the difference between a macro and an inlined function?

Sean

"Marius S." <mars centras.lt> wrote in message
news:3CC4A2FB.3000403 centras.lt...

I don't like the idea of separate syntax for the macro language.
I think it would be more convenient to have similar syntax to D
and special statements indicating macros.
For example something like this:

macro void test(int arg)
{
   printf("a = %d;\n", arg);
}

what do you think?
you then could write
"macro a" or "macro{ a b }" and maybe "macro: a" (like "public")
just a thought... :)

Roland wrote:


right. to be usefull, arg must not be typed: macro void test(arg) { . . }

I'd say arg coud be typed too and i think there should be some type which matches all types (like "var" in java script).
 so what is the difference with template function ?
 none
 exept that it could be parcical to have some more directives like symbole
 name concatenation,
 repeat statement directive, local symbole names, etc..
 
 macro void opeq(dim,p,op,a) {
     int i;
    for (i=0; i<dim; i++) {    //dim is a constant. compiler may unfold the
 loop
         p.coord#i op a;         //# concatenate symbols. p.coord0 op a;
 p.coord1 op a...
     }
 }

It could be more powerfull (but maybe less right) if its output would be compiled, then you could generate more flexible source code with it :) ...like PHP in HTML... no, i think that's not right :) sorry.
 
 now what does it do ?
 example:
 
 void afunction(point3d& p; const int a) {
     .
     .
     opeq(3,p,-=,a);
     .
     .
 }
 
 expand like
 
 void afunction(point3d& p; const int a) {
     .
     .
      p.coord0 -= a;
      p.coord1 -= a;
      p.coord2 -= a;
     .
     .
 }
 
 ???????
 
 personaly i made my mind: complex macros should be processed externally from
 the compiler.
 just i have to find a good preprocessor for that. any suggestions ?
 
 roland

Apr 23 2002
prev sibling parent reply DrWhat? <blackmarlin nospam.asean-mail.com> writes:
Roland wrote:

 DMACROS
 
 There is still a domain not already specified in D is replacement for
 macros / Templates.
 I think D cannot afford not to study MASM style of macros (oops i mean
 ABEL HDL style of course).
 
 Questions are:
 
 1- is it usefull / powerfull ?

powerful - yes, though most of this could be done another way (it may be your example and macros may be the only way to do some things - though I cannot think of any examples.
 2- is it readable / easy to maintain ?

No - it took me 3 passes before I understood it (and I have written these style of things before). More importantly it does not match well with the style of D coding. Consistancy is the first step towards a good language.
 3- is it easy to implement/parse ?

Yes if written from scratch - adding this functionality to the current D parser may not be so easy.
 4- is it easy to debug ?

As the code is not easy to read I would say no. Further more relating generated code to macro definitions is a none trivial task. ///=================================================================================
 
 Example of pseudo-code using masm style macro.

One word : Ewwwwwww! Though if implemented as a entirely seperate preprocessor / translator which knows nothing about D or any other language (or maybe even built into an integrated development environment) it could could find a useful niche - for example this could be very helpful when writing assembler - especially as syntax between assembler an the macros is not very different. Such a seperate preprocessor could help generate repetitive blocks of code - may be writing one in D as an example of a complex project may be a good idea. I would not recommend such macros to be added to D - in my opinion the times when they are useful are far outweighed by both the additional complexity and their difficulty to learn. Their addition would be a major feature, though I think an infrequently used one. C 2002/4/16 PS: #FOR ??i = 1, ??i < %%dim, ??i++ ///I think you guess what #FOR/#ENDM macro-directive do I am guessing this is like the VHDL / Ada for .. generate statement.
Apr 16 2002
parent reply Roland <rv ronetech.com> writes:
DrWhat? a écrit :

 Example of pseudo-code using masm style macro.

One word : Ewwwwwww!

I was sure you will say that. That mean difficult isn't it ? (hope you read the .H with an idde that highlight the preprocessor directives) Even if for me it is reasonably readeable, i understand it is difficult to understand. I didnt bet on the succes of this suggestion. Even i'm not sure I would favor this for D. Just i think that is a possibility among other to do the job that has to be taken in consideration for its power and compacity. It is an other phylosophie. That code expand a lot. Me, I prefer to hurt my brain with few lines of complicate code than use my eyes on a lot of line of simple code. In fact, if not easier, it is faster to write, and, i pretend it is easier to fix. roland
Apr 17 2002
next sibling parent "Walter" <walter digitalmars.com> writes:
"Roland" <rv ronetech.com> wrote in message
news:3CBDB98F.718A29B5 ronetech.com...
 Even if for me it is reasonably readeable, i understand it is difficult to

 I didnt bet on the succes of this suggestion. Even i'm not sure I would

 D.
 Just i think that is a possibility among other to do the job that has to

 consideration for
 its power and compacity.

I see no problem with writing a macro processor that generates D as output. A text processing macro language will not work within D, however, because of the requirement of separation of passes. Having a macro expansion assemble a token, for example, breaks this. It will also break the feature that D can be pre-tokenized.
Apr 17 2002
prev sibling parent C.R.Chafer <blackmarlin nospam.asean-mail.com> writes:
Roland wrote:

 
 
 DrWhat? a écrit :
 
 Example of pseudo-code using masm style macro.

One word : Ewwwwwww!

I was sure you will say that. That mean difficult isn't it ? (hope you read the .H with an idde that highlight the preprocessor directives) Even if for me it is reasonably readeable, i understand it is difficult to understand. I didnt bet on the succes of this suggestion. Even i'm not sure I would favor this for D. Just i think that is a possibility among other to do the job that has to be taken in consideration for its power and compacity.

Yes, that is why I suggested using this style for a SEPERATE (and generic) preprocessor.
 It is an other phylosophie.

No problem with that - if we did not try different philosophies we would never improve out current position.
 That code expand a lot.
 Me, I prefer to hurt my brain with few lines of complicate code than use
 my eyes on a lot of line of simple code.

I tend to aggree though a few lines of complicated code may often mushroom into a lot of lines of complex code.
 In fact, if not easier, it is faster to write, and, i pretend it is easier
 to fix.

Due to the thinking time involved to verify it is correct - I find the two either complex or simple styles similar with respect to production time. Though frequently the complex styles execute faster - however this is not the case with macro processors and therefore the implementation of such a processor should have simple syntax. C 2002/4/18
Apr 17 2002