Index a #define string

J

Julie

David said:
On Tue, 20 Apr 2004 08:37:59 -0700 in comp.lang.c++, Julie


In a sense, yes. But the discussion is pretty pointless for me if I
have to supply all the answers. It doesn't get me any closer to
understanding what you or Christopher or Paul might have in mind.

I answered as well.
C++ took a big step forward when it took a step away from the
preprocessor and toward compile-time metaprogramming integrated with the
language. We are not all the way there yet, and cannot yet abandon
macros completely in C++. Perhaps we should say that macros are a
"necessary evil."

What we _should_ say is:

The preprocessor, specifically including macro expansion, is a language
construct that should be fully understood prior to using in production-quality
code.

That's it -- it isn't good, it isn't bad, it just is.
 
D

David Harmon

On Tue, 20 Apr 2004 10:46:03 -0700 in comp.lang.c++, Julie
I answered as well.

Yes you did; and I apologize if I appeared to discount that.
I responded with what I did not like about offsetof(), and if I had to
base my opinion only on NULL and offsetof() then I would have to hold
that preprocessor macros were an unmitigated evil and had no place in
C++ programming. Which of course I do not.
What we _should_ say is:

The preprocessor, specifically including macro expansion, is a language
construct that should be fully understood prior to using in production-quality
code.

That's it -- it isn't good, it isn't bad, it just is.

Instead we strive to build language facilities so that their hazards are
not so blatant, ones that can be used in comparative safety while using
only the parts you think you understand.

On another thread recently we had a comparison of

If anybody has a reason to prefer the #define macro for something like
that, I have yet to hear about it. And that is with a #define macro
written by someone who fully understands the hazards; the naive first
attempt:
#define MAX(a, b) (a > b) ? a : b
causes trouble sooner rather than later.

Show me a macro and a inline template function that accomplish the same
purpose, apples to apples, and then show me a reason not to prefer the
function 100% of the time.
 
I

Ioannis Vranos

David Harmon said:
On another thread recently we had a comparison of

To give a small help, inline is better to be used:

template<class T>
inline const T& MAX(const T& a, const T& b) { return (a > b ? a : b); }


Here we go.






Regards,

Ioannis Vranos
 
J

Julie

David said:
On Tue, 20 Apr 2004 10:46:03 -0700 in comp.lang.c++, Julie


Yes you did; and I apologize if I appeared to discount that.
I responded with what I did not like about offsetof(), and if I had to
base my opinion only on NULL and offsetof() then I would have to hold
that preprocessor macros were an unmitigated evil and had no place in
C++ programming. Which of course I do not.

I figured that others would have responded w/ other similar utility macros, but
pretty much all I heard was the crickets...
Instead we strive to build language facilities so that their hazards are
not so blatant, ones that can be used in comparative safety while using
only the parts you think you understand.

Right -- education is the key. Understand macros, understand templates,
understand whatever and use the construct that is most appropriate.
On another thread recently we had a comparison of


If anybody has a reason to prefer the #define macro for something like
that, I have yet to hear about it. And that is with a #define macro
written by someone who fully understands the hazards; the naive first
attempt:
#define MAX(a, b) (a > b) ? a : b
causes trouble sooner rather than later.

Show me a macro and a inline template function that accomplish the same
purpose, apples to apples, and then show me a reason not to prefer the
function 100% of the time.

Without further information, I'll prefer the inline/template fn over the macro
any day. I'm not advocating macros /over/ equivalent inline funcs. I've
mostly been talking about cases where there is no equivalent template function.
 
I

Ioannis Vranos

Julie said:
Right -- education is the key. Understand macros, understand templates,
understand whatever and use the construct that is most appropriate.


I do not wish to upset you once again, but 0 is better to be used instead of
NULL for pointers (just to make sure you have realised that), and macros are
appropriate only where unavoidable (which probably you have not realised it
yet).






Ioannis Vranos
 
J

Julie

Ioannis said:
I do not wish to upset you once again, but 0 is better to be used instead of
NULL for pointers (just to make sure you have realised that), and macros are
appropriate only where unavoidable (which probably you have not realised it
yet).

I use NULL as that is what is provided by the language. How it is actually
implemented is up to the compiler and library writer.

For me, it is much more clear to describe pointers in terms of NULL as opposed
to 0, both in code, when describing code, and when talking about code w/
others. If you prefer 0, so be it.

You might want to take a quick nap and then review what I've said in previous
posts. Nowhere do I blindly advocate macros over an equivalent non-macro
implementation -- I have stated that any construct (including macros) should be
used where appropriate.
 
I

Ioannis Vranos

Julie said:
I use NULL as that is what is provided by the language. How it is actually
implemented is up to the compiler and library writer.


Ok, but in C++ it is as if you return EXIT_SUCCESS in main() instead of
return 0; Well do it as you wish.

For me, it is much more clear to describe pointers in terms of NULL as opposed
to 0, both in code, when describing code, and when talking about code w/
others. If you prefer 0, so be it.

You might want to take a quick nap and then review what I've said in previous
posts.


I have awakened two hours ago.


Nowhere do I blindly advocate macros over an equivalent non-macro
implementation -- I have stated that any construct (including macros) should be
used where appropriate.



Yes but you disagreed for my messages suggesting avoid the use macros as
much as possible.






Ioannis Vranos
 
P

Paul Mensonides

David said:
On Tue, 20 Apr 2004 03:56:40 -0700 in comp.lang.c++, "Paul Mensonides"


Sure you are. After several back-and-forth you have not posted one
single #define to illustrate a single one of your claims. I go to the
files you list:

I have posted references to well-respected libraries. I can certainly post a
simple example, but it would only be a toy, such as:

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/control/expr_if.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>

#ifndef ADD_MAX_ARITY
#define ADD_MAX_ARITY 100
#endif

#define item(z, n, _) \
BOOST_PP_EXPR_IF(n, +) BOOST_PP_CAT(p, n) \
/**/

#define add(z, n, _) \
template<class T> inline T add( \
BOOST_PP_ENUM_PARAMS( \
BOOST_PP_INC(n), T p \
) \
) { \
return BOOST_PP_REPEAT(BOOST_PP_INC(n), item, ~); \
} \
/**/

BOOST_PP_REPEAT(ADD_MAX_ARITY, add, ~)

#undef item
#undef add

As I said, this example is nothing but a toy, but nevertheless, with a tiny
amount of lateral thought, it is illustrative. It defines 100 function
overloads that add a series of numbers, such as:

template<class T> inline T add(T p0) {
return p0;
}

template<class T> inline T add(T p0, T p1) {
return p0 + p1;
}

template<class T> inline T add(T p0, T p1, T p2) {
return p0 + p1 + p2;
}

// etc.

Assuming that the "add" function is useful--which it generally isn't--it takes
two user-defined macros and one line of code to generate the equivalent of 300+
lines of source code.
Not a #define in sight except for
#if !defined(BOOST_SPIRIT_RULE_HPP)
#define BOOST_SPIRIT_RULE_HPP

template <
BOOST_PP_ENUM_PARAMS(
BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT,
typename ScannerT
)struct scanner_list : scanner_base {};

This is preprocessor metaprogramming. It is expanding the library as needed by
a user based on BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT. In this case specifically,
if BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT is 3 for example, it generates:

template <
typename ScannerT0, typename ScannerT1, typename ScannerT2struct scanner_list : scanner_base {};
Not a #define in sight except for
#if !defined(BOOST_SPIRIT_RULE_IPP)
#define BOOST_SPIRIT_RULE_IPP

The entire rest of the file after the second line that says:

#if BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT > 1

is all preprocessor metaprogramming stuff--namely macro definitions that are
repeated, etc., by Boost.Preprocessor, which is a preprocessor metaprogramming
library. As above, this is generating code that expands the capacity of the
library.
Not present in the version of Spirit I am using.


Not present in the version of Spirit I am using.

These two are in the latest Boost CVS. However, I believe with Spirit, the
Boost CVS lags behind the separate Spirit CVS (though I'm not absolutely sure of
that). They aren't, AFAICS, in the current Spirit CVS.
I give up. It is like "Read the encyclopedia and you will see what I
mean."

I'm not going to explain the entire field of generative preprocessor
metaprogramming to you. I've already given you the basic idea: using the
preprocessor to generate, among other things, repetitious code. Because you
fail to see the obvious--like all the inclusions of Boost.Preprocessor
files--doesn't mean that it isn't there--in Spirit as well as all the other
libraries that I mentioned and more besides.

This is the exact result of bias without reason and is precisely why guidelines
such as "avoid macros" are dangerous. That generalization is invalid. It needs
to be broken down into more specific guidelines that aren't based on an abstract
feeling of paranoia and fear caused by many ill uses of the preprocessor in the
past.
Total floating abstract nonsense. Source code is what comes out of
your keyboard; and it includes whatever preprocessor macros you
write. If
you "generate" it, it wouldn't be *source*.

That is completely ridiculous. If a macro expands to some C++ code, it is
reasonable to call the process "generating source code." Obviously, by virtue
only of pedantic technicality, it isn't actually the source code itself.
Further, *of course* the above is abstract, since there are already plenty of
concrete examples that I already pointed out to you that do actual real work
instead of a toy example that I could provide here. Take a look at any file at
all in Boost--including Spirit and Fusion--that includes a
So, in trying to fathom what, if anything, you are talking about, I
run
in to the following comment from Joel. Kinda sums it all up:

Yes, and if you actually read it, you'd know what it is talking about
specifically: using a macro to generate a function for a specific type. Of
course that's a hack to work around instantiation depth problems and, as such,
is less than ideal. All other things being equal--of course this is a worse
solution.

How about this other quote from Joel in a conversation that I was having with
him and Hartmut a while back:

Joel:
Kinda sums it all up, but there's more (and these are from the Boost list so you
can find them in case you don't believe me--again):

Joel:
Here's another one...

Someone:

And another one...

Joel:
Maybe you think that you know everything that you need to to make such
generalizations, but you need to realize that there are *a lot* of good uses of
the preprocessor that have nothing to do with include guards, assertions, or
preprocessor "variables".

Regards,
Paul Mensonides
 
I

Ioannis Vranos

Paul Mensonides said:
As I said, this example is nothing but a toy, but nevertheless, with a tiny
amount of lateral thought, it is illustrative. It defines 100 function
overloads that add a series of numbers, such as:

template<class T> inline T add(T p0) {
return p0;
}

template<class T> inline T add(T p0, T p1) {
return p0 + p1;
}

template<class T> inline T add(T p0, T p1, T p2) {
return p0 + p1 + p2;
}

// etc.


Couldn't the above be simply a variadic function?






Ioannis Vranos
 
P

Paul Mensonides

Ioannis said:
To give a small help, inline is better to be used:

template<class T>
inline const T& MAX(const T& a, const T& b) { return (a > b ? a :
b); }


Here we go.

No argument from me here. For something like this you're totally right.

Actually, there is one argument, but more like a general reminder: thou shalt
not use all caps identifiers for anything but macros.

Regards,
Paul Mensonides
 
P

Paul Mensonides

Ioannis said:
I do not wish to upset you once again, but 0 is better to be used
instead of NULL for pointers (just to make sure you have realised
that), and macros are appropriate only where unavoidable (which
probably you have not realised it yet).

What you haven't realized yet, and refuse to realize, is that macros can
sometimes drastically increase code quality, lower maintenance costs, and do
many other helpful things. Your "guideline" is nothing but a fear of a few
problem-causing uses generalized across an entire suite of possible applications
which are not problem-causing. That is the antithesis of
"thinking-outside-the-box."

Regards,
Paul Mensonides
 
P

Paul Mensonides

Ioannis said:
Couldn't the above be simply a variadic function?

First, the example is not intended to be useful. It is intended merely to be a
toy example.

Second, taking the utility of the above "add" functions as a base assumption for
the moment, even if there is a better way that the above could be done without
macros (it would require a different interface and probably higher-order
functions), I can simply construct a different example which cannot. (Consider
a function_traits template in which you'd want to extract the types of each
individual parameter--maybe to make a regular function pointer "adaptable".
That is a lot of repetitive code to handle a reasonable maximum arity--and
repetitive code is error prone and drastically increases maintenance points.)

Third, if it was variadic, you would lose type safety, whereas with the above,
you lose none.

Regards,
Paul Mensonides
 
I

Ioannis Vranos

Paul Mensonides said:
Third, if it was variadic, you would lose type safety, whereas with the above,
you lose none.


Well it could be done by simply taking as arguments two iterators, one to
the beginning and one to past the end element. Also there is such a function
in standard library!






Ioannis Vranos
 
I

Ioannis Vranos

Paul Mensonides said:
Actually, there is one argument, but more like a general reminder: thou shalt
not use all caps identifiers for anything but macros.

You are right, and for constants.






Ioannis Vranos
 
I

Ioannis Vranos

Paul Mensonides said:
Your "guideline" is nothing but a fear of a few
problem-causing uses generalized across an entire suite of possible applications
which are not problem-causing.


And again, i would rather see those applications done with non-macro code if
possible.







Ioannis Vranos
 
I

Ioannis Vranos

Ioannis Vranos said:
Well it could be done by simply taking as arguments two iterators, one to
the beginning and one to past the end element. Also there is such a function
in standard library!

.... but only for sequences. I was thinking sequences at that moment.






Ioannis Vranos
 
D

David Harmon

On Tue, 20 Apr 2004 18:34:38 -0700 in comp.lang.c++, "Paul Mensonides"
I have posted references to well-respected libraries. I can certainly post a
simple example, but it would only be a toy, such as:

#include <boost/preprocessor/arithmetic/inc.hpp>

[Big snip]
As I said, this example is nothing but a toy, but nevertheless, with a tiny
amount of lateral thought, it is illustrative.

It certainly is. This is great stuff, and I have never seen anything
quite like it in many years of C and C++ programming. I don't know yet,
but it appears to extend the much lamented, crippled C preprocessor to
things that C programmers have asked for for decades.

Would it have killed you to say "boost/preprocessor" at the beginning of
the thread? Did I have to beat it out of you or something?
Assuming that the "add" function is useful--which it generally isn't

two user-defined macros and one line of code to generate the equivalent of 300+
lines of source code.

This is partially the result of a shortcoming in the language, where I
would like to see some form type-safe variable argument list that
doesn't exist. But I haven't studied the boost/preprocessor
documentation yet, and I'm sure there are deeper examples.

[another big snip]
I'm not going to explain the entire field of generative preprocessor
metaprogramming to you. I've already given you the basic idea: using the
preprocessor to generate, among other things, repetitious code.

You don't have to explain the entire field; this is the first time you
have thrown me a crumb. Would it have killed you to write
"boost/preprocessor"?
This is the exact result of bias without reason and is precisely why

No, it's the result of you being unable to say what you mean, of going
on and on and around and around about generalities like generating
source code without giving a single specific. But I'm very grateful
that you actually did have one in mind after all, and that you have
finally broken down and pointed me at it. I will now go away and read
up on it, and learn something.
 
P

Paul Mensonides

Ioannis said:
You are right, and for constants.

No, not for constants. Use all caps identifiers for one thing and one thing
only--macro names. Constants, which I assume you to mean constant variables or
enumerators, should definitely *not* be all caps--otherwise you just reintroduce
the likelihood of a name clash.

Regards,
Paul Mensonides
 
P

Paul Mensonides

Ioannis said:
... but only for sequences. I was thinking sequences at that moment.

It doesn't matter. The example was not intended to be anything but illustrative
of the _potential_ for good use of the preprocessor. I was purposely attempting
to keep it simply so that people don't have to understand both the concepts to
which I refer and the purpose and utility of the desired results. Originally, I
was going to write a full blown function_traits or maybe a closure mechanism,
but then the point would have been lost in the details.

You are certainly correct regarding the STL and sequences, etc., and their
utility in general. Those things are not lost on me. However, consider, for
example, that the standard library has a notion of an "adaptable" function
object, which is one that has the appropriate typedefs. In particular, pointers
to functions and references to functions are not, according to the standard
library, adaptable. In reality, they are, but the implementation requires
repetition--a great deal of it. This is one of many places that preprocessor
metaprogramming can be brought to bear with excellent effect--and you lose none
of the type safety or integration of the core language nor, if you do it as you
should, raise any issues regarding name clashes whatsoever.

Regards,
Paul Mensonides
 
P

Paul Mensonides

David said:
On Tue, 20 Apr 2004 18:34:38 -0700 in comp.lang.c++, "Paul Mensonides"
I have posted references to well-respected libraries. I can
certainly post a simple example, but it would only be a toy, such as:

#include <boost/preprocessor/arithmetic/inc.hpp>

[Big snip]
As I said, this example is nothing but a toy, but nevertheless, with
a tiny amount of lateral thought, it is illustrative.

It certainly is. This is great stuff, and I have never seen anything
quite like it in many years of C and C++ programming. I don't know
yet,
but it appears to extend the much lamented, crippled C preprocessor to
things that C programmers have asked for for decades.

In some ways, yes. Boost.Preprocessor is not the top of the line in the field
though--it is crippled by poor preprocessor implementations in popular
compilers. Chaos is *significantly* more advanced, but I haven't "officially"
released it yet. (I hate writing docs!)
Would it have killed you to say "boost/preprocessor" at the beginning
of
the thread? Did I have to beat it out of you or something?

I was attempting to address the "macro == evil" point, not heavy-duty
preprocessor metaprogramming, which is merely *a* good use of the preprocessor
(though it isn't always a good use). Moreover, I was attempting to address the
underlying issue of coding-by-guideline, which is the same as
coding-by-generalization. That is fundamentally dangerous. It invites lack of
thought, and it minimizes experimentation which is usually the basis for
powerful new techniques.
Well, we had an answer involving std::plus<>() here not too long ago.

It was only for the sake of argument, I personally have never defined (except
here) any such set of function overloads just to add parameters together.
Unless there was a really good exterior reason for doing so, I would deem that a
bad usage of preprocessor metaprogramming. In other words, what I've said goes
both ways. Just because macros can be used for good does not mean that one
should strive to use macros *just* for that reason, nor should one strive to
avoid macros *just* because they can be (and have been and will continue to be)
used poorly. The issue is much more complex than that, and that requires
experience with the available constructs--so you know not only when *not* to use
them, but also when *to* use them.
This is partially the result of a shortcoming in the language, where I
would like to see some form type-safe variable argument list that
doesn't exist. But I haven't studied the boost/preprocessor
documentation yet, and I'm sure there are deeper examples.

Variadic templates/argument lists are under consideration for C++0x. That will
certain drop off the number of required uses of preprocessor metaprogramming,
but by no means all. Personally, I'm really hoping that we get something like
that.
[another big snip]
I'm not going to explain the entire field of generative preprocessor
metaprogramming to you. I've already given you the basic idea:
using the preprocessor to generate, among other things, repetitious
code.

You don't have to explain the entire field; this is the first time you
have thrown me a crumb. Would it have killed you to write
"boost/preprocessor"?

I was saying exactly what I meant: using the preprocessor to generate, among
other things, repetitive code. To me that is entirely clear--even without
knowing how it is done. What bothers me, is the assumption that everything that
needs to be known about macros is known, and nothing more is required to
denounce them (except in special cases such as include guards, assertions, and
compiler workarounds). I find that attitude arrogant (i.e. "I know everything
that needs to be known to make pronouncement X."), and I in turn start acting
arrogant. Apologies for the latter.
No, it's the result of you being unable to say what you mean, of going
on and on and around and around about generalities like generating
source code without giving a single specific. But I'm very grateful
that you actually did have one in mind after all, and that you have
finally broken down and pointed me at it. I will now go away and read
up on it, and learn something.

I pointed to examples used in real practical code, which I thought would be
better examples of utility than toy examples. From your mention of Spirit,
which is indeed a great library, I thought that examination of Spirit itself,
given your respect for it, would be more likely to alter your perception than
anything that I might say here. That is why I refrained from giving toy
examples, and why I pointed to other usages. It is always easy to provide toy
examples of something, but not always easy to prove their viability in practice.
As a preprocessor metaprogramming expert, one of a very few, I'm constantly
having to deal with this kind of blind adherence (not specifically directed at
you, but in general) that sometimes it wears on me, and I get frustrated.
Please accept my apologies for that.

Regards,
Paul Mensonides
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top