Topic: Clarified usage of proposed preprocessor directives
Author: brianatrimble@att.net (Brian Trimble)
Date: Tue, 1 Oct 2002 13:04:49 +0000 (UTC) Raw View
Let me clear up the basics of typedmacro and mixmacro first then provide an
explanation
of my view on "inline" followed by more complete examples to flesh out my view
of these new
directives.
#typedmacro is a strictly typed version of #define macros with all the
pasting implied therein.
Thus it provides pseudo-functions which are defined internally(a new idea) or
externally to other
functions and then are pasted together by the compiler. Hence if I define two
loop automaters:
#typedmacro IncIntLoop(int idx, int length); for(idx=0; idx<length;
idx++) #endmacro
#typedmacro DecIntLoop(int idx,int length); for(idx=length-1; idx>=0;
idx--) #endmacro
then use them in contrived code:
IncIntLoop( t,10 ); { DecIntLoop(t,10); { if(t==9) break; } }
it will look ugly but will absolutely resolve to:
for(t=0; t<10; t++) { for(t=9; t>=0; t--) { if(t==9) break; } }
so indeed the programmer will have to know what occurs within some of the
unencapsulated
macros, just like with other macros. The primary difference for #typedmacro is
that the arguments
to it will be checked and the implementation would probably chop the arguments
into variable names
such that IncIntLoop(t++,10) would be flagged as an error or a warning of code
having no effect.
#mixmacro is a combination of typedmacro and normal macros in providing
typed and untyped
arguments. This provides a new kind of macro where variable names and text can
be inserted and
checked. Bracketing some arguments would allow the explicit seperation of
pastable text from type
checked arguments. To see these features at work I will point to foreach in
it's myriad forms. With
#define you can try to declare foreach as:
#define forEach(start,end, code) while(start!=end){ code; start++; }
and then get all kinds of errors if you call it with:
intUse = &(intarray[0]); intEnd = &(intarray[20]);
forEach( intUse, intEnd, printf("%d\n",*intUse);
localIntVector.add(*intUse) )
hence the creation of the foreach template in <algorith> and it's callback
format. So some forms of
generic coding and code reuse can be stymied by the need for function calls and
the inability to
completely integrate the generic code into other functions. If forEach was
declared as a mixmacro:
#mixmacro forEach([start],[end],[code]); while(start!=end){ code start++;
} #endmacro
then called with:
forEach( [intUse], [intEnd], [printf("%d\n",*intUse);
localIntVector.add(*intUse); ] );
would translate directly to:
for(intUse!=intEnd){ printf("%d\n",*intUse); localIntVector.add(*intUse);
intUse++; }
thus maintaining local scope, stack, and incurs no penalty by calling any new
functions. It is ugly,
prone to possible breaking, and completely ignores the issue of templates but
does point out how
tight code integration can achieved by mixmacros in a way that template and
#define
meta-programming cannot easily replicate.
Now onto the matter of "inline". The keyword inline is a request to the
compiler to try and
inline the function into a calling function according to the compilers' rules.
As "inline" is a request, it
may be ignored depending on speed/size optimization and difficulty in
translating it. To wit my
Borland C++ 5.01 compiler gives me warnings which state that functions
containing for/while, whether
they are standalone or class member functions, are not expanded inline. Next up
is the fact that inline
functions are incapable of providing the kind of code integration I showed
above. Another will certainly
correct me but I believe that if I define a function as:
inline void mul(int &n, int multiple){ n=multiple*n; }
then the best translation I can hope for with a call of mul(a,10); would be:
int __multiple=10; int *__n=&a; *__n=(__multiple * (*__n));
which is a valid solution for most cases where inline is used but leaves me with
new variables and less
control over the final code. In contrast:
#typedmacro mul(int n, int multiple); n=multiple*n; #endmacro
with a call of mul(a,10); could resolve to: a=10*a;
whereby I know exactly how the symbols will resolve and I am assured that the
code is inline regardless
of its' content. Borland defines other rules for when it ignores inline
requests and I'm certain the other
compilers out there have similar exclusion laws, hence these new macros are the
only way to get around
them and maintain some type checking. Hopefully this illuminates why I cannot
completely accept "inline".
Now onto an example which displays some of the power I see in having a
triumvirate of macros. Here
I will provide code segments for a program which tests some line drawing
algorithms. Assume all the line
algorithms have the signature lineName( Pt&, Pt&, int color). Also assume the
idea of internally defined
macros has been implemented.
struct Pt{ int x, y; };
///Line Algorithms
#define WIDTH 800
#define HEIGHT 600
int main(void)
{
Pt p1, p2;
int y;
//setup graphics and ancillary variables...
#define LineTest( Name, Color) for(y=0; y<HEIGHT; y++){ p2.y=y; line##Name
(p1,p2,Color); }
p1.x=0; p1.y=0; p2.x=WIDTH-1;
LineTest(Mine,1) LineTest(RunSlice,2) LineTest(Wu,3) LineTest(Fraction,4)
///close graphics...
return 0;
}
A classic use of #define which ties the definition closely to the variables.
Now if I want to control which
variables are used, ensure their type, and provide a better test I would recode
it as:
int main(void)
{ Pt p1, p2; int x, y;
//setup...
#typedmacro LineSet(int xc, int yc, Pt pt);
for(yc=0; yc<HEIGHT; yc++)
{ for( xc=0; xc<WIDTH; xc++) { pt.x=xc; pt.y=yc;
#endmacro
#define CloseLineSet } }
#define Line(Name,color) line##Name (p1,p2,color)
p1.x=0; p1.y=0;
LineSet(x,y,p2); Line(Mine,1); CloseLineSet
LineSet(x,y,p2); Line(RunSlice,2); CloseLineSet
LineSet(x,y,p2); Line(Wu,3); CloseLineSet
LineSet(x,y,p2); Line(Fraction,4) CloseLineSet
///close...
return 0;
}
Though it is not the greatest vindication for typedmacro, one can see the type
checking and ability to change
variable names at work in this example. Now onto the power of mixmacro:
int main(void)
{ Pt p1, p2; int x, y; LARGE_INTEGER t1,t2;
//setup...
#mixmacro LineTest(int xc, int yc, Pt pt, [PreText], [ActionCode],
[PostText]);
for(yc=0; yc<HEIGHT; yc++)
{ for( xc=0; xc<WIDTH; xc++) { pt.x=xc; pt.y=yc; PreText ActionCode
PostText } }
#endmacro
#define Line(Name,color) line##Name (p1,p2,color)
p1.x=0; p1.y=0;
LineTest(x,y,p2,[],[ Line(Mine,1) ], [ ]);
LineTest(x,y,p2,[],[ Line(RunSlice,2); ], []);
LineTest(x,y,p2,[ p1.y=y; ],[ Line(Wu,3) ],[]);
p1.y=0;
LineTest(x,y,p2,[QueryPerformanceTimer(&t1);],[ Line(Fraction,4)
],[QueryPerformanceTimer(&t2);]);
///close...
return 0;
}
which translates to:
int main(void)
{ Pt p1, p2; int x, y; LARGE_INTEGER t1,t2;
//setup...
p1.x=0; p1.y=0;
for(y=0; y<600; y++)
{ for(x=0; x<800; x++){ p2.x=x; p2.y=y; lineMine(p1,p2,1); } }
for(y=0; y<600; y++)
{ for(x=0; x<800; x++){ p2.x=x; p2.y=y; lineRunSlice(p1,p2,2); } }
for(y=0; y<600; y++)
{ for(x=0; x<800; x++){ p2.x=x; p2.y=y; p1.y=y; lineWu(p1,p2,3); } }
p1.y=0;
for(y=0; y<600; y++) //taking some formatting liberties
{
for(x=0; x<800; x++)
{ p2.x=x; p2.y=y; QueryPerformanceTimer(&t1);
lineFraction(p1,p2,4); QueryPerformanceTimer(&t2); }
}
///close...
return 0;
}
Now one sees most of mixmacro's desired power in providing a way of making
meta-functions with
variable functionality while ensuring that all desired type checking is
enforced. Also note how the
nature of the testing loops can be changed via the inclusion of text areas which
can just be white space.
Though these examples certainly do not answer all questions, I hope they show
why these new
preprocessor directives would be valuable to C++ in general.
Thanks for any further consideration,
Brian Trimble ( brianatrimble@att.net )
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]