Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

What does style look like, part 4

What does style look like, part 4

Rate This
  • Comments 33
Continuing the discussion of "style"..

Yesterday, I showed the code reformatted into "BSD" style format, which looks like:

#include "list.h"
main(C cArg, SZ rgszArg[])
{
    I iNode;
    I cNodes = atoi(rgszArg[1]);
    I cNodesToSkip = atoi(rgszArg[2]);
    PNODE pnodeT;
    PNODE pnodeCur;
    InitNodes(cNodes);
    for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
    {
        pnodeT = PnodeNew(iNode);
        InsertNext(pnodeCur, pnodeT);
        pnodeCur = pnodeT;
    }
    while (pnodeCur != PnodeNext(x))
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
        {
            pnodeCur = PnodeNext(pnodeCur);
        }
        FreeNode(PnodeDeleteNext(pnodeCur));
    }
    printf("%d\n", Item(nodeCur));
}

I chose one of the variants that I personally find most attractive, any one of them could work. The thing about this example is that there are no comments.  That's reasonable given that it's an example from a textbook, and as such the words in the textbook that accompany the example suffice as the documentation.  But all code begs to be documented, so lets see what happens.

When you look at documenting a function, there are a couple of things to keep in mind when considering style (there are other important things, like the relevance of the comments, etc, but this post's about style).

The first question that comes to mind, almost immediately is "What commenting form should you use?"  For C/C++, there are a number of options.

First off, C and C++ support two different styles of comment.  The first is the traditional  /* */ style of comments.  And there's the C++ style // comment.  Each of the two comments have different flavors.

When I was in college, I loved using some of the options that /* */ comments enabled.  My code was just full of things like:

         :
         :
    }

    /*************************************************************/
    /*                                                           */
    /*                                                           */
    /*   Loop through the nodes looking for the current node.    */
    /*                                                           */
    /*                                                           */
    /*************************************************************/
    while (pnodeCur != PnodeNext(x))
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
         :
         :
 

I also had this thing about putting a comment on every line, especially for assignments.  And every comment lined up on column 40 if possible.

         :
         :
    }

    /*************************************************************/
    /*                                                           */
    /*                                                           */
    /*   Loop through the nodes looking for the current node.    */
    /*                                                           */
    /*                                                           */
    /*************************************************************/
    while (pnodeCur != PnodeNext(x))    // Keep looping until PnodeNext == pnodeCur
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++) // Skip through cNodesToSkip nodes
         :                             // A comment for this line.
         :                             // A comment for that line.
 

When I look back at my old code, the code screams "1980's", just like the hairdos of "A Flock of Seagulls" does.  But there are times when big honking block comments like that are appropriate, like when you're pointing out something that's REALLY important - like when you're about to do something that would cause Raymond Chen to swear like a sailor when he runs into your application.

Also, while I'm on the subject of comments on every line.  There ARE situations where you want a comment on every line.  For example, if you're ever forced to write assembly language (for any assembly language), you really MUST comment every line.  It's far too easy to forget which registers are supposed to contain what contents, so adding comments on every line is essential to understanding the code.

/* */ comments have the disadvantage that they take up a fair amount of space.  This means that they increase the effective line length.  // comments don't have this problem.  On the other hand, /* */ comments stand out more.

Personally I feel that both comment styles should be used.  One fairly effective form that I've seen is:

         :
         :
    }
    /*
     * Loop through the nodes until you hit the current node.
     */
    while (pnodeCur != PnodeNext(x))
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
        {
            pnodeCur = PnodeNext(pnodeCur);  // Skip to the next node.
        }
         :
         :
 

Of course that's not very different from:

         :
         :
    }

    // Loop through the nodes until you hit the current node.
    while (pnodeCur != PnodeNext(x))
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
        {
            pnodeCur = PnodeNext(pnodeCur);  // Skip to the next node.
        }
         :
         :
 

On the other hand, the first form (/**/) stands out more, simply because it has more white space.  You can achieve the same whitespace effect with // comments:

         :
         :
    }
    //
    // Loop through the nodes until you hit the current node.
    //
    while (pnodeCur != PnodeNext(x))
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
        {
            pnodeCur = PnodeNext(pnodeCur);  // Skip to the next node.
        }
         :
         :
Another aspect of commenting style is the effective line length.  When I learned to program, I learned on 80 character wide terminals, which imposed a fairly strict limit on the number of columns in the code - anything longer than 80 characters was not OK.  Nowadays, that isn't as important, but it's still important to limit source code lines to a reasonable number, somewhere around 100 works for me, but of course your experience might be different.

The other major thing about comments is the content of the comments.  Comments can be the bane of a maintainers existence or their savior, depending on the comment.

I don't know how many times I've come across the following and cringed:

    //
    // Increment i.
    //
    i = i + 1;
 

Yes, the line's commented, but how useful is the comment?  Especially since it's a block comment (block comments have more importance than in-line comments by virtue of the amount of real estate they occupy - the very size of the comment gives it weight.  In general, you want to use in-line comments for little things and block comments for the big stuff.  I almost never use in-line comments, I prefer to use block comments at the appropriate locations and let the code speak for itself.

I've mentioned white space as an aspect of style before, but this is the time to bring it up.  Consider the example routine with some white space added to stretch the code out a bit:

#include "list.h"
main(C cArg, SZ rgszArg[])
{
    I iNode;
    I cNodes = atoi(rgszArg[1]);
    I cNodesToSkip = atoi(rgszArg[2]);
    PNODE pnodeT;
    PNODE pnodeCur;

    InitNodes(cNodes);

    for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
    {
        pnodeT = PnodeNew(iNode);
        InsertNext(pnodeCur, pnodeT);
        pnodeCur = pnodeT;
    }

    while (pnodeCur != PnodeNext(x))
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
        {
            pnodeCur = PnodeNext(pnodeCur);
        }
        FreeNode(PnodeDeleteNext(pnodeCur));
    }

    printf("%d\n", Item(nodeCur));
}

It's not a major change, but to me, by just inserting 4 empty lines, the code's been made clearer. 

The other aspect of white space is intra-expression white space.  This is one of the aspects of style that tends to get religious.  I've seen people who do:

    for ( iNode = 2 , pnodeCur = PnodeNew( 1 ) ; iNode <= cNodes ; iNode ++ )
And others who prefer:

    for (iNode=2,pnodeCur=PnodeNew(1);iNode<=cNodes;iNode++)
Or:

    for (iNode=2, pnodeCur=PnodeNew(1); iNode<=cNodes; iNode++)
Or:

    for ( iNode=2, pnodeCur=PnodeNew(1) ; iNode<=cNodes ; iNode++ )
The reality is that it doesn't really matter which form you use, as long as you're consistent.

Lets see what happens to the sample routine if you insert some block comments...

#include "list.h"
main(C cArg, SZ rgszArg[])
{
    I iNode;
    I cNodes = atoi(rgszArg[1]);
    I cNodesToSkip = atoi(rgszArg[2]);
    PNODE pnodeT;
    PNODE pnodeCur;

    InitNodes(cNodes);

    //
    // Create a list of cNodes nodes. 
    // 
    for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
    {
        pnodeT = PnodeNew(iNode);
        InsertNext(pnodeCur, pnodeT);
        pnodeCur = pnodeT;
    }

    //
    // Walk the list of nodes, freeing the node that occurs at every cNodesToSkip nodes in the list.
    //
    while (pnodeCur != PnodeNext(x))
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
        {
            pnodeCur = PnodeNext(pnodeCur);
        }
        FreeNode(PnodeDeleteNext(pnodeCur));
    }

    //
    // Print out the value of the current node.
    //
    printf("%d\n", Item(nodeCur));
}

To me, that's a huge improvement.  By stretching out the code and adding some comments, it's already starting to look better.

Again, just for grins, here's how it would look in my "1980's style":

#include "list.h"
main(C cArg, SZ rgszArg[])
{
    I iNode;                              // Node index.
    I cNodes = atoi(rgszArg[1]);          // Set the number of nodes
    I cNodesToSkip = atoi(rgszArg[2]);    // and the nodes to skip.
    PNODE pnodeT;                         // Declare a temporary node.
    PNODE pnodeCur;                       // And the current node.

    InitNodes(cNodes);                    // Initialize the nodes database
                                          // with cNodes elements

    /*************************************************************/
    /*                                                           */
    /*                                                           */
    /*               Create a list of cNodes nodes.              */
    /*                                                           */
    /*                                                           */
    /*************************************************************/
    for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
    {
        pnodeT = PnodeNew(iNode);        // Allocate a new node.
        InsertNext(pnodeCur, pnodeT);    // Insert it after the current node
        pnodeCur = pnodeT;               // Current = New.
    }

    /*************************************************************/
    /*                                                           */
    /*                                                           */
    /*   Walk the list of nodes, freeing the node that occurs    */
    /*   at every cNodesToSkip nodes in the list.                */
    /*                                                           */
    /*                                                           */
    /*************************************************************/
    while (pnodeCur != PnodeNext(x))    // While not at the current node
    {
        for (iNode = 1; iNode < cNodesToSkip ; iNode++)
        {
            pnodeCur = PnodeNext(pnodeCur); // Current = current->Next
        }
        FreeNode(PnodeDeleteNext(pnodeCur)); // Free current->Next
    }

    /*************************************************************/
    /*                                                           */
    /*                                                           */
    /*          Print out the value of the current node.         */
    /*                                                           */
    /*                                                           */
    /*************************************************************/
    printf("%d\n", Item(nodeCur));
}

Yech.

Tomorrow: function and file headers.

  • Wow, I'm loving that 1980s style!

    And with regards to formatting, the one thing I love about VS 2005 is the auto formatting, and customisation of this formatting. Whenever I press return at the end of a line, it is formatted just how I like, and the same when I close a curly brace. Fantastic.
  • Heh, 80's style, Larry where did you first see that style of commenting at to where you picked it up? I know I used it and picked it up somewhere off of a Microsoft Piece of code or example somewhere, liked the style and used it when going to college in the late 80's.

    Also one thing kind of left out is the XML commenting style for C# While similar to C there is XML in there in specific orders to help build code documentation
  • TOPS-20 Emacs (the original Emacs, predating Gnu/Emacs) had a Pascal mode that allowed you to write:
    (** *)
    (** Stuff *)

    and then run an emacs macro that would expand it to the appropriate range. I fell in love with it (thinking it was "cool") and used it everywhere I could.

    You're right about XML comments, but they're language specific (and this discussion is intended to be language agnostic, even though I'm framing it in C code).
  • That 80's style brings me warm memories about direct video memory access libraries :-)
  • I, personally, have a strict rules for C/C++:

    1. only // is for comments
    2. // is only for comments
    3. only /**/ is for disabling code
    4. /**/ is only for disabling code

    That is very handy: finally, do you ever write a really multiline comment? Never. So // is OK. But when you have do disable some, say, ifthenelse piece (especially while debugging), it's very convenient to use /**/

    And, also for debuggin I like using things like this:

    //*
    code (code, "code");
    //*/

    (In case someone doesn't know, this kind of comment you can turn on/off by just removing/adding the initial /)

    In PHP, there's also are # comments, and my PHP editor, EditPlus, has a fantastic feature to syntax-highlight different comments differently. What I do is I make //'s grey and #'s bright red. So #'s are for important comments, like

    # CHECK IF $_POST'ED DATA IS OK!
    $a = @$_POST['something'];

    Sorry for being too verbose :)
  • Actually, Illya, I often write multiline comments, especially if something tricky's going on.

  • <quote>finally, do you ever write a really multiline comment</quote>

    Every day on every method. I do at least. Been caught before by the Go back and add this to your code you wrote 6 years ago. And I sit there look at the code and say What the heck was I thinking there. Then change it and break 50 other things I also coded 6 years ago.Then say Ohhhh thats why I did that. Get caught by that a few times you will write more comments.

    More and more I even include snippets of code on how to use methods and classes right in the code as well.
  • Code is actually an implementation of some of your ideas. When you see the idea clearly, your comments are usually guiding you through the implementation, like
    "Check if file is accessible" or "Add fulltext index to column C".

    If you have written the code 6 years ago, it's very likely that you don't remember the idea, sure. But I do not think that comments is a great place to express your ideas. Comments are needed to describe the actual steps you make.

    When I have some "tricky" algorythm, I just go to my Wiki site or even open MSWord and describe it there and save within a project website/folder.

    Well, that's how things work for me. Maybe it's because my longest code is 200 KB? :-)
  • In VS, I use a non-default background color for comments (gray. forecolor= black).
    It does a really good job at improving readability.
    e.g. when you're scrolling down the code looking for a specific piece, it's really easy to read the comments without paying attention to the code. It's just not in your way !

    Also, it's really easy to see the beginning of code pieces thanks to 'the gray line above them'.

    My 2 cents,
  • Ahhh, Ok, sorry I mistook your you statement about one line comments thinking that was all the documentation you do. My appologies, yes I used to do this as well. But as I commented earlier I use C# commenting and I guess I have been using it for a couple years now. Which allows for a lot of documentation like the word stuff your doing to be written right in your code. C# and .net has really changed my entire outlook and styles of writing code.

    And Sorry Larry not meaning to turn this thread into language specific but the C# rules are something I feel so strongly about I can not resist. Like you said before, your boss says your forthright, I said sometimes I am strongly opinionated.

    So by following the C# documentation guidelines I do not need to open word, I do not need write additional documentation. I also when going back in time as I have had to go back and modify code written in early .net I do not have to search for documents, I have access to the code and can see all the documentation in one nice file. And I can change the documentation as I change the code.

    Now the kicker, with tools like NDoc I can produce full chm files ready to read in a very organized and readable format. I do not produce these chm files for myself, but for others. I personally read the the documentation in my code. Now who others? well this I actually amazed myself. Many people have heard of Sarbanes oxley. Well aparently I impressed someone somewhere to write an article on my code documentation and Sarbanes compliance with it. With the C# XML style and using it exactly how Microsoft recomends I have blown auditors minds when they come in to look at it they are used to seeing reams of paper, books diagrams and so on. And the best thing is, I never used anything other than Visual studio and NDoc to pass all my code through several audits.

    Link to an article I was interviewed on about longhorn but turned more into a .net article for me
    http://www.adtmag.com/article.asp?id=10162


    See the following link for specifics on the C# commenting.
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vcoriXMLDocumentation.asp?frame=true

    Again sorry Larry not trying to jump your post but the XML commenting I really truely believe is a revolutionary idea and process and style in code commenting. For me at least this is one thing that has permanently changed the way I write code. Much the way you look at the 80's style and say I can't believe I did it this way is how I look at all code comments anymore. The XML style has just grabbed me like a monkey grabs a banana.
  • Larry, are you going to mention tools like Doxygen in this series? I ask because code-documentation systems tend to impose commenting/formatting rules of their own.
  • Myron, in fact, the next post in the series mentions AutoDOC, which I suspect is essentially the same thing.

    Jeff, I'm not trying to belittle the C# XML comments - they'are actually pretty amazing (IIRC, Chris Brumme indicated that the public CLR reference manual is actually generated from the CLR sources). It takes Doxygen and AutoDOC and DocJet to a totally different level.
  • How the .NET SDK and other SDK's are created is in fact written about here: http://blogs.msdn.com/loripe (guess that's what 'the public CLR ref. manual' is?).

    Cool that they're using reflection for part of the job. It's really a big opportunity to get a lot of boring doc-work done by the computer(which seems to always love doing the boring-work!).

    And yeah, it really sucks reading code where every line is documented(guess I've commited that crime myself). It's often easier to just delete those comments and then read the code :)
  • > And there's the C++ style // comment.

    I think BS knowingly and wilfully inherited that comment style from BCPL.

    Sometime around 1996 or 1997 or so, some novice computer reviewer wrote an article about the Macintosh, saying that it has a Windows 95-like user interface. Of course the Macintosh's user interface was copied, but it was copied from a famous copier company, not copied from that reviewer's familiar copy of a copy of the famous copier company's work.

    However, when C copied //, I think it did so because of the influence of C++ rather than the influence of BCPL. If BCPL had been sufficiently influential in commenting style, then C would have had it from C's start. (Hmm, which did B have, I've forgotten.)

    11/12/2004 12:59 PM Ilya Birman

    > 3. only /**/ is for disabling code
    > 4. /**/ is only for disabling code

    That must make it pretty tough to disable code that was written by anyone whose style differs from yours, or written before C copied // style comments.

    I use #if 0 to disable code. It also requires a single-character change (to #if 1) to temporarily reenable it during debugging. And it nests.
  • Ilya, you're technique of using // for comments and /**/ for disabling a code block is a good idea. Note to self: start using this technique.
Page 1 of 3 (33 items) 123