August 18, 2001
All this talk reminds me (no offense to the Unreal developers!) of the Win32 API.  Basically, you have some struct xyz, and XYZ is a pointer to an xyz struct.  Sometimes.  Or maybe it's just a UINT32.  Or a PASCAL FAR.  Who knows?

I don't know about you guys, but I've certainly wasted a lot of time digging through MSVC documentation trying to figure out what in the world the API's macros map to!  Win32 in and of itself stands out--in my mind at least--as a terrible misuse of the preprocessor, and makes me really want a language that completely lacks macros and makes abuses like these at least inconvenient, compared to the Right Way. <wink>

You guys who want to lecture us about how _good_ the Win32 API is, or how great it was for its time or whatever, flame away, I guess I've baited the hook...

 - Brent


August 20, 2001
Here is one more use: Hide the complexities which you can't change..

int main(ARGS){
    if( ! ARG_PRESENT(1) )fatal("Usage: crlfd infile [outfile]");
    SepFileName in_name = ARG(1), out_name;
    if(!ARG_PRESENT(2) ){
        out_name = in_name;
        in_name.prepareBackup();    // Delete old .bak, rename this to .bak
        in_name.ChangeExt("bak");
        }
    else out_name = ARG(2);

I guess similarly the preprocessor can be used to build an efficient 'facade' for a readymade library. (Deriving a class just to redefine a few inline functions is too much work, plus this might require casting to base class...)

Again, consider the following use to hide ugly initilisers: (I have even forgotton the underlaying structures:)

#define CFNAME "cards.dat"
#define LOGFILE "c:\\cust.log"
#define PRODUCT "Unknown Product"

#define DTLANG  "dtlang.h"
#define DTCUST  "dtcust.c"

char *CustLog;
char *custdbase, *makecmd, *makeparam1, *makeparam2, *makeparam3=0;
char *orgname, *newname, *product, *hdrout, *dataout;

INITABLE it[] = {
    INISTR( "Files",    "log",      CustLog,    LOGFILE , 0),
    INISTR( 0,          "custdbase",custdbase,  CUSTDBASE , 0),
    INISTR( 0,          "orgname",  orgname,    0 , 0),
    INISTR( 0,          "newname",  newname,    0 , 0),
    INISTR( 0,          "makecmd",  makecmd,    MAKECMD , 0),
    INISTR( 0,          "make1",    makeparam1, MAKEPARAM1 , 0),
    INISTR( 0,          "make2",    makeparam2, MAKEPARAM2 , 0),
    INISTR( 0,          "product",  product,    PRODUCT, 0),
    INISTR( 0,          "header",   hdrout,     DTLANG, 0),
    INISTR( 0,          "datafile", dataout,    DTCUST, 0),
    INIEND(),
    };

int main(ARGS){
    if( ! processini( CFNAME, it)){
        printf("Ini processing failed..");
        return 0;
        }
    ....


Tim Sweeney <tim@epicgames.com> wrote in message news:9lkf0r$2nh0$1@digitaldaemon.com...
> > Okay, why is it that everybody thinks the C preprocessor is terrible and needs to be avoided?
>
> Because it's terrible and needs to be avoided, of course!
>
> > C++ only has it for compatability; Java and C# and D
> > don't have one.  I just don't get this one.
>
> Historically (beginning with C), macro preprocessors have served as a
kludge
> to compensate for a lack of language power and optimizer ability.  Macros were good an necessary in the C days, because you could often come pretty close to C++ style template programming if you were clever enough, using macros for casts and other tricks.  You could also use them to construct optimized code (i.e. by defining the body of a loop in a macro, and unrolling the loop 100 times with the macro).
>
> With a modern language, these kludges should not be necessary.  If there
is
> any particular case where you feel you could express a program more
cleanly
> by using macros, then I recommend looking at that as a language or
optimizer
> flaw to be fixed, and not a need for macros.
>
> Unreal (the game) is probably a good production-code example.  It's around 250,000 lines of code and uses macros for the following:
>
> 1. To expose metaclass information (i.e. class names, default constructors that a serializer can call) -- like MFC's techniques.  All of this code would be unnecessary if the language supported classes as first-class objects (i.e. you can pass around a classref* which "represent" the class and exposes its static functions), static virtual functions, and static constructors.
>
> 2. To comment out large blocks of code.  Would be unnecessary if /*...*/ comments could be nested.
>
> 3. To implement debug-specific code.  This is actually unnecessary, a bad old habbit.  We would be just as well off having a global constant debug=0 or 1, and having if(debug) {...} instead of #if debug.
>
> 4. To implement platform-specific headers.  Only necessary because headers are necessary.
>
> 5. To perform template-style tricks.  If the language has a great facility for type dependency (whether like C++ templates, or more general like Haskell), all of these things would be unnecessary.  Even C++ templates aren't complete enough, i.e. there are no template typedefs (true type synonyms), and most production compilers have bizarre template bugs
limiting
> what you can do.
>
> I'm 100% sure the Unreal code would be simpler and cleaner if the language supported the above and all preprocessor support were eliminated.
>
> -Tim
>
>


August 20, 2001
(Interesting to see you in here, Tim!)

"Tim Sweeney" <tim@epicgames.com> wrote in message news:9lkf0r$2nh0$1@digitaldaemon.com...
> > Okay, why is it that everybody thinks the C preprocessor is terrible and needs to be avoided?
>
> Because it's terrible and needs to be avoided, of course!

:-)

> > C++ only has it for compatability; Java and C# and D
> > don't have one.  I just don't get this one.
>
> Historically (beginning with C), macro preprocessors have served as a
kludge
> to compensate for a lack of language power and optimizer ability.  Macros were good an necessary in the C days, because you could often come pretty close to C++ style template programming if you were clever enough, using macros for casts and other tricks.  You could also use them to construct optimized code (i.e. by defining the body of a loop in a macro, and unrolling the loop 100 times with the macro).
>
> With a modern language, these kludges should not be necessary.  If there
is
> any particular case where you feel you could express a program more
cleanly
> by using macros, then I recommend looking at that as a language or
optimizer
> flaw to be fixed, and not a need for macros.

Here's one: compile time macros as a substitute for carrying a freight-train-load of object type information into the executable.

I was just reading about C#'s printf function, which works because everything is an object carrying it's type information around at run time. It doesn't need format specifiers.

If this trend continues, I see C remaining as dominant an implementation language as it is today, because it's the only popular language that concentrates on run-tim efficiency.

> Unreal (the game) is probably a good production-code example.  It's around 250,000 lines of code and uses macros for the following:
>
> 1. To expose metaclass information (i.e. class names, default constructors that a serializer can call) -- like MFC's techniques.  All of this code would be unnecessary if the language supported classes as first-class objects (i.e. you can pass around a classref* which "represent" the class and exposes its static functions), static virtual functions, and static constructors.

Java.

> 2. To comment out large blocks of code.  Would be unnecessary if /*...*/ comments could be nested.

..or if your editor had a function that would insert or remove // comment markers over a selected range of text (emacs can do this).  Still more ways to alleviate language issues...

> 3. To implement debug-specific code.  This is actually unnecessary, a bad old habbit.  We would be just as well off having a global constant debug=0 or 1, and having if(debug) {...} instead of #if debug.

What about:

class NastyClass
{
#if DEBUG
    int tracehistory[512];
#endif

> 4. To implement platform-specific headers.  Only necessary because headers are necessary.

Yeah, well, I agree some things shouldn't be preprocessor jobs; #include'd common definitions, #define'd types and constants are among them.

> 5. To perform template-style tricks.  If the language has a great facility for type dependency (whether like C++ templates, or more general like Haskell),

I'll have to go read up on Haskell.

> all of these things would be unnecessary.  Even C++ templates
> aren't complete enough, i.e. there are no template typedefs (true type
> synonyms), and most production compilers have bizarre template bugs
limiting
> what you can do.
>
> I'm 100% sure the Unreal code would be simpler and cleaner if the language supported the above and all preprocessor support were eliminated.

My main issue is that the macro language should give compile-time power to the programmer, with access to the type system and symbol tables.  So code can be generated depending on variable types, on aggregate membership, etc.

--
Richard Krehbiel, Arlington, VA, USA
rich@kastle.com (work) or krehbiel3@home.com (personal)



August 20, 2001
In article <9lqfvs$8pm$1@digitaldaemon.com>, Rajiv Bhagwat wrote:
> Here is one more use: Hide the complexities which you can't change..
> 
> int main(ARGS){
>     if( ! ARG_PRESENT(1) )fatal("Usage: crlfd infile [outfile]");
>     SepFileName in_name = ARG(1), out_name;
>     if(!ARG_PRESENT(2) ){
>         out_name = in_name;
>         in_name.prepareBackup();    // Delete old .bak, rename this to .bak
>         in_name.ChangeExt("bak");
>         }
>     else out_name = ARG(2);

This is ugly.  *PLEASE* don't teach this to anybody.  This *hides* necessary things, things which should be seen and understood.


As an aside.  I've worked at a certain company (who shall remain nameless), where macros were used in C, to provide certain OO things. Among them were macros for almost anything imaginable.  The record number of characters per line (after macro expansion) was in excess of 4K worth of characters.  Needless to say, these expressions were neither free of side-effects, nor particularly fast (the original intention), nor bug-free.  I've had compilers silently truncate such long lines.  And by pure luck, have them compile, and the program largely work (1 failure case in 10000's of test cases).


That being said, macros can be good, and they can be evil.  The above is the beginning of evil.  If you need macros to hide that sort of inline complexity, write a new function to abstract the behaviour. Give it a reasonable name, and use it.  In that way someone coming along in 5 years will be able to understand what you did, and will not have to worry about side-effects, etc.

--Toby.
August 20, 2001

Richard Krehbiel wrote:
> What about:
> 
> class NastyClass
> {
> #if DEBUG
>     int tracehistory[512];
> #endif

FWIW, already addressed in the existing D spec:

  class NastyClass
  {
      debug
      {
         int tracehistory[512];
      }
      ...


-Russell B
August 20, 2001

Tobias Weingartner wrote:
> 
> In article <9lqfvs$8pm$1@digitaldaemon.com>, Rajiv Bhagwat wrote:
> > Here is one more use: Hide the complexities which you can't change..
> >
> > int main(ARGS){
> >     if( ! ARG_PRESENT(1) )fatal("Usage: crlfd infile [outfile]");
> >     SepFileName in_name = ARG(1), out_name;
> >     if(!ARG_PRESENT(2) ){
> >         out_name = in_name;
> >         in_name.prepareBackup();    // Delete old .bak, rename this to .bak
> >         in_name.ChangeExt("bak");
> >         }
> >     else out_name = ARG(2);
> 
> This is ugly.  *PLEASE* don't teach this to anybody.  This *hides* necessary things, things which should be seen and understood.

Funny, I was just about to make a case for command-line parameter access to be built into the language, to hide even the need for these macros.

I don't see the above example as being any uglier than the traditional argc/argv mess.

-Russell B
August 20, 2001
Tim Sweeney wrote:
>...
> 
> 2. To comment out large blocks of code.  Would be unnecessary if /*...*/
> comments could be nested.
> 
>...
> 
> -Tim
> 
Nested comments are very dangerous, unless the comments are labelled. Then it might be a good idea.  Otherwise the tag deletions could become quite confusing if any mistakes were made.

consider the possibility of:
/*!*  ... /*&*   ... *&*/ ... *!*/

etc. however.  Practically any character should be useable in the "flag" position.  And one letter is probably sufficient, though if it weren't excessive I suppose one could extend it to be any run of non-whitespace not including an asterisk.  That would let one say, e.g.:

/*NoFileFound* ... /*FileOpenFailed* ... /*IOError* ... *IOError*/ ... *FileOpenFailed*/ ... *NoFileFound*/

But use of this would require specialized tools.

IDEA!!
Since the program text can already contain embedded html (XML ??) code anyway, what about using html/XML comment tags to comment out code?

Or, if XML is used, and it is desired that commented out code be visible, then one could specify a labelled XML tag for the comment boundaries.  It's a bit more complex than the simple scheme of above, but it would allow more or less standard tools to do the processing.

August 20, 2001
Charles Hixson wrote:
> 
> Tim Sweeney wrote:
> >...
> >
> > 2. To comment out large blocks of code.  Would be unnecessary if /*...*/ comments could be nested.
> >
> >...
> >
> > -Tim
> >
> Nested comments are very dangerous, unless the comments are labelled. Then it might be a good idea.  Otherwise the tag deletions could become quite confusing if any mistakes were made.
> 
> consider the possibility of:
> /*!*  ... /*&*   ... *&*/ ... *!*/

Eeek!

I'm gonna say it one more time: the One True Way to comment out large blocks of code is with // at the beginning of the line. You can even tag and nest these:

//none of this works//   codecode();
//none of this works//   morecodecode();
//none of this works////this doesn't work yet//   stillmorecodecode();
//none of this works////this doesn't work yet//   blah();
//none of this works////this doesn't work yet////was://
otherkindofblah();
//none of this works////this doesn't work yet//   finish();

In practice, I use shorter tags than this; typically things like:

//ref//    code or declarations from some other part of the program, //ref//    kept around for quick reference

//obs//    obsolete code kept for reference or rollback

//RAB//    my initials; "I have a truly marvelous reason for commenting //RAB//    this out, which this margin is too narrow to contain."

//no//     any other random comment-out reason

//         very temporary comment-outs

A good configurable editor should let you make this sort of comment- out easy to do; a syntax-highlighting editor will make it clear that this is all commented out, which most don't do with an #if 0 block.

If your editor (or hard copy) isn't syntax-highlighted, this is also clearer than commenting out a large block with /* ... */.

-Russell B
August 21, 2001
Russell Bornschlegel wrote in message <3B814102.B41B493E@estarcion.com>...
>I don't see the above example as being any uglier than the traditional argc/argv mess.


The D way of doing command lines:

    import stdio;

    int main(char[][] args)
    {
        for (int i = 0; i < args.length; i++)
        {
            printf("Argument %d = '%.*s'\n", i, args[i]);
        }
    }

The .*s is a hack to get C printf to print D strings that don't have a terminating 0. The char[][] is an array of strings.


August 21, 2001
Russell Bornschlegel <kaleja@estarcion.com> wrote in message news:3B814102.B41B493E@estarcion.com...
>
>
> Tobias Weingartner wrote:
> >
> > In article <9lqfvs$8pm$1@digitaldaemon.com>, Rajiv Bhagwat wrote:
> > > Here is one more use: Hide the complexities which you can't change..
> > >
> > > int main(ARGS){
> > >     if( ! ARG_PRESENT(1) )fatal("Usage: crlfd infile [outfile]");
> > >     SepFileName in_name = ARG(1), out_name;
> > >     if(!ARG_PRESENT(2) ){
> > >         out_name = in_name;
> > >         in_name.prepareBackup();    // Delete old .bak, rename this to
.bak
> > >         in_name.ChangeExt("bak");
> > >         }
> > >     else out_name = ARG(2);
> >
> > This is ugly.  *PLEASE* don't teach this to anybody.  This *hides* necessary things, things which should be seen and understood.

It certainly simplifies the use. The capitalisation clearly marks the use of macros. I have used getarg(), even classes to parse the command lines and ultimately found that for simple cases, the use of these macros is best. (BTW, I don't recollect from where I have picked them).

What is wrong about hiding the complexities? The whole of OO paradigm is based on it. It is not necessary to fret about every implementation detail - I just need to know if the user has supplied the 2nd argument or not. Using a class or a function for a simple filter would be overkill.

>
> Funny, I was just about to make a case for command-line parameter access to be built into the language, to hide even the need for these macros.
>
> I don't see the above example as being any uglier than the traditional argc/argv mess.
>
> -Russell B

Also, no one has commented about the use of macros to init data structures
as presented in the second part. Maybe the use is not common enough. Oh, I
think MFC does something similar all the time to declare the message map
data structures.
-- Rajiv