I've done that. The problem is that it isn't visually explicit.
Personally I would want to know that X is between Y and Z. But back when I was doing assembly I would have thought Y is between X and Z. Of course, being able to look at the code or specifications gives the answer, but given all the things there are to remember...
I would have to look it up every time I saw it --which gets old fast.
It is also a #define, which I've learned can bite you in dangerous ways. For example, what if I say:
1 2 3 4
|
int n = 19;
// Is the lower byte of n between 50 and 200?
if (isBetween( n & 0xFF, 50, 200 ))
apparently();
|
It fails because operator precedence boogered the equation. Of course this example makes it obvious, but how often are the quantities so neatly given or known beforehand?
So we try to fix it with:
#define isBetween(A, B, C) ( (((A)-(B)) > -ZERO) && (((A)-(C)) < ZERO) )
That'll solve the operator precedence problem, but it leaves you wide open for this:
1 2 3
|
int n = 100;
while (isBetween( n = recalc( n ), 50, 90 ))
cout << n << endl;
|
Once again, the programmer has to know
exactly how the macro is defined to avoid it, and he has to write something more obtuse like
1 2
|
while (n = recalc( n ), isBetween( n, 50, 90 ))
cout << n << endl;
|
which makes no sense to anyone but those who remember that
isBetween is a macro trap. Someone, months or years from now, will say, "what?" and 'fix' it as they go about their duties. Then spend months of maintenance time trying to figure out where the error is.
The following site is the first one that popped up for me in Google, but it is part of the standard CPP documentation which lists all these problems with macros and more:
http://developer.apple.com/documentation/DeveloperTools/gcc-4.0.1/cpp/Macro-Pitfalls.html#Macro-Pitfalls
Another problem I have with it is that it doesn't template on anything but numeric types that can promote to floating point --essentially throwing away all the power you get from operator overloading. I can't, for example, say:
1 2
|
string names[] = { "derrik", "alexis", "hannah" };
if (isBetween( names[0], names[1], names[2] )) ...
|
I would have to go the long way around and do the macro's magic myself.
if ((names[1] < names[0]) && (names[0] < names[2])) ...
Using code templates enable you to have: type checking, namespace management, non-numeric ordered types, and more succinct code.
I have settled with defining two ranger constructors:
ranger() and
R(), and letting the user import the one he wants. So I can say:
1 2
|
if (R( 0 ) <= index < length_of_x)
foo = x[ index ];
|
and
1 2
|
string name = "derrik";
if (R( "alexis" ) < name < "hannah") ...
|
Sure, it still has that funny-looking 'R' or 'ranger' in there, but the code is instantly readable and the purpose and use of R can be surmised without having to refer to its documentation.
Hence the time I spent developing it.
Whew. Hope I answered your question...