November, 2004

Larry Osterman's WebLog

Confessions of an Old Fogey
  • Larry Osterman's WebLog

    How did we make the DOS redirector take up only 256 bytes of memory?

    • 18 Comments
    In one of my early posts, I mentioned a status review we had with BillG for the DOS Lan Manager redirector (network filesystem).

    I also talked to Robert Scoble about this in the last of my Channel9 videos.  One thing that somehow got missed in both the original article (later updated) and the video was our reaction to Bill's feedback.

    The simple answer is that we fixed the problem.  My team didn't do much with the transports and network drivers (because they were out of our scope), but we were able to do something about the footprint of the redir.exe program (it was a T&SR application).

    When we were done with it, I managed to shrink the below 640K running size of redirector to 128 bytes in size, beyond which I couldn't figure out how to go.

    The question that obviously comes up is: How did you manage to do that?  Raymond, please forgive me for what I'm about to disclose, for within this tale lie dragons.  This discussion is for historical purposes ONLY.  I don't recommend it as a practice.

    The MS-DOS redirector was actually originally written as a part of the MSDOS.SYS (IBMDOS.COM) binary.  For obvious reasons (not every user in the world had a network card, especially in 1984), the redirector was split out from the DOS binary after the product shipped.  In fact, when I took over the redirector project, the binary used to link with hundreds of unresolved external symbol errors (because the redirector linked with some, but not all of the MS-DOS binaries).  One of the first things that I did while working on the project was to clean this up so that the redirector would cleanly link without relying on the MS-DOS objects.  But being a part of MS-DOS, it was written in "tiny" mode - the code and data were commingled internally.

    The first thing I did when trying to shrink the footprint of the redirector was to separate the code and data segments.  Today, this seems utterly obvious, but in 1986, it was a relatively radical idea, especially for real-mode software.  Once I had split the data and code, I was able to make the data segment relocatable.  This change was critical, because it enabled us to do a boatload of things to reduce our footprint.  One thing to keep in mind about the redirector was that even though the data (and eventually code) was relocatable, the motion wasn't dynamic.

    The first thing I did was to lay the redirector's code and data as follows:

    Code

    Initialization Code

    Data

    Initialization Data/Dynamic data (allocated after startup)

    By laying out the code and data this way, I could slide the data over the initialization code after the initialization was done.  It wasn't that much of a real savings, however, since the original redirector simply started the dynamic data at the start of the initialization code (and left it uninitialized).

    The next thing to do was to take advantage of a quirk in the 286 processor.  The 8086 could only address one megabyte of memory, all the memory above 640K was reserved for system ROMs. (A quick aside: DOS could (and did) take advantage of more than 640K of RAM - DOS could address up to 1M of RAM, all the processor could support if it wasn't for the system ROMs.  In particular, there were several 3rd party memory cards that allowed mapping memory between 640K and 0xB0000, which was the start of video memory).  With the addition of the 286 processor, the machine could finally address more than 1M of RAM.  It turns out that if the machine had more than 640K of RAM, most systems mapped the memory above 640K to above 1M.  Unfortunately, there were a number ofapplications that depended on the fact that the 8086 could only address 1M of RAM, and performed arithmetic that assumed that physical address 0xFFFF0+0x30=0x000020.  To control this, the PC/AT and its successors defined a software controllable pin called the "A20 line" - if it was disabled , memory access between 1M and 1M+64K was redirected to 0, if it was enabled , then it was mapped to real memory.  This is really complicated, but the effect was that if you enabled the A20 line, an application could have access to 64K of additional memory that didn't impact any running MS-DOS applications!  This 64K was known as the "High Memory Area", or HMA.

    Because the powers that be knew that this would be a highly contentious piece of real estate (everyone would want to party on it), Microsoft (or Intel, or IBM, I'm not sure who) wrote a specification and a driver called HIMEM.SYS.  HIMEM.SYS's purpose was to arbitrate access to that 64K chunk of RAM.

    Well, for the DOS Lanman redirector, we wanted to use that area, so if we were able to reserve the region via himem.sys, we moved the data (both dynamic and static) up to that memory.  On every entry to the redirector, we enabled the A20 line (via himem.sys), and on every exit, we disabled the A20 line.

    That saved about 30K of the 60K MS-DOS footprint, so far so good. 

    The next step in the process was to remove our dependencies on himem.sys.  Around this time, Lotus, Intel and Microsoft had defined a specification for an expanded memory manager, known as LIM.  This allowed a 3rd party memory card to bank swap memory into the 0xA0000->0xFFFFF memory region.  Marlin Eller joined the team about that time, and he wrote the code to move the data segment for the DOS redirector into LIM (if himem.sys wasn't available, and LIM was).  After finishing that work, he moved on to other projects within Microsoft.  That's where things stood for Lan Manager 1.5, the data had been removed, but nothing else.  A HUGE improvement, but we weren't satisfied.

    So far, we were just moving the data around, we hadn't done anything to deal with the 30K or so of code.

    The next thing we did was to split the redirector up still further:

    "Low" code
    "Low" data

    Code

    Initialization Code

    Data

    Initialization Data/Dynamic data (allocated after startup)

    We added a low code and data segment.  The "low" code segment contained all the external hooks into the redirector (interrupt handlers, etc), and code to enable the HMA and LIM segments.  We then moved the data into LIM memory, and the code into the HMA.  This was a bit trickier, but we managed.

    So we now had a low code segment that was about 2K or so, and the code and data was moved up out of the 640K boundary.  Normally, I'd be satisfied with this, but I love a challenge.

    The next step was to look long and hard at the low code.  It turns out that most of the low code didn't really NEED to be low, it was just convenient.  Since the code had been moved into the HMA, all I needed to do was to have a low-memory stub with enough code to enable the HMA, and dispatch to the corresponding function in high memory.

    The other thing I realized was that the MS-DOS PSP (Program Segment Prefix, the equivalent of a task in MS-DOS) contained 128 bytes of OS stuff, and 128 bytes of command line (this is where Raymond starts cringing).  Since the redirector didn't use the command line, I figured I could re-use that 128 bytes of memory for my stub to enable the high memory area.  And that's what I did - I used the 128ish bytes of command line to hold the interrupt dispatch routines for all the entrypoints to the redirector (there were about 4 of them), and pointers to the corresponding routines in the high memory area, and the code to enable (and disable) the HMA.

    And voila, I had a 0 footprint redirector.  The only negative that came from this was that applications that enumerated the "running" processes didn't handle the "code-in-the-command-line" thing.

    Btw, the work I did here was pretty much totally clean.  I used the linker to define the segments that were relocated, I didn't do any of the other sleazy things that MS-DOS programmers did to make their code small (like combining multiple instructions together relying on the relative offset of a jump instruction to form the first byte of a different instruction).  It was actually a pretty cool piece of work.

    Oh, and this description doesn't really give the full flavor of what had to be done to get this to work.  A simple example: Because I had to handle moving the data over the code that was performing the move - that meant that I need to first move the initialization code out of the way (past the end of the data), jump to the moved initialization code, move the data over the original initialization code, then terminate the application.

    But we eventually (for Lan Manager 2.2) had a 0 footprint redirector.  It took some time, and it didn't work for every configuration, but we DID make it work.

     

  • Larry Osterman's WebLog

    What does style look like, part 4

    • 33 Comments
    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.

  • Larry Osterman's WebLog

    What does style look like, part 7

    • 37 Comments
    Over the course of this series on style, I've touched on a lot of different aspects, today I want to discuss aspects C and C++ style specifically.

    One of the things about computer languages in general is that there are often a huge number of options available to programmers to perform a particular task.

    And whenever there's a choice to be made while writing programs, style enters into the picture.  As does religion - whenever the language allows for ambiguity, people tend to get pretty religious about their personal preferences.

    For a trivial example, consider the act of incrementing a variable.  C provides three different forms that can be used to increment a variable. 

    There's:

    • i++,
    • ++i,
    • i+=1, and
    • i=i+1.

    These are all semantically identical, the code generated by the compiler should be the same, regardless of which you chose as a developer (this wasn't always the case, btw - the reason that i++ exists as a language construct in the first place is that the original C compiler wasn't smart enough to take advantage of the PDP-8's built-in increment instruction, and i++ allowed a programmer to write code that used it).

    The very first time I posted a code sample, I used my personal style, of i+=1 and got howls of agony from my readers.  They wanted to know why on EARTH I would use such a stupid construct when i++ would suffice.  Well, it's a style issue :)

    There are literally hundreds of these language specific style issues.  For instance, the syntax of an if statement (or a for statement) is:

    if (conditional) statement

    where statement could be either a single line statement or a compound statement.  This means that it's totally legal to write:

    if (i < 10)
        i = 0;

    And it's totally legal to write

    if (i < 10)
    {
        i = 0;
    }

    The statements are utterly identical from a semantic point of view.  Which of the two forms you choose is a style issue.  Now, in this case, there IS a fairly strong technical reason to choose the second form over the first - by putting the braces in always, you reduce the likelihood that a future maintainer of the code will screw up and add a second line to the statement.  It also spaces out the code (which is a good thing IMHO :) (there's that personal style coming back in again)).

    Other aspects of coding that ultimately devolve to style choices are:

    if (i == 10)

    vs

    if (10 == i)

    In this case, the second form is often used to prevent the assignment within an if statement problem - it's very easy to write:

    if (i = 10)

    which is unlikely to be what the developer intended.  Again, this is a style issue - by putting the constant on the left of the expression, you cause the compiler to generate an error when you make this programming error.  Of course, the compiler has a warning, C4706, to catch exactly this situation, so...

    Another common stylistic convention that's often found is:

    do {
        < some stuff >
    } while (false);

    This one exists to allow the programmer to avoid using the dreaded "goto" statement.  By putting "some stuff" inside the while loop, it enables the use of the break statement to exit the "loop". Personally, I find this rather unpleasant, a loop should be a control construct, not syntactic sugar to avoid language constructs.

    Speaking of goto...

    This is another language construct that people either love or hate.  In many ways, Edsger was totally right about goto - it is entirely possible to utterly misuse goto. On the other hand, goto can be a boon for improving code clarity.  

    Consider the following code:

    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method2();
            if (hr == S_OK)
            {
                hr = myCOMObject->Method3();
                if (hr == S_OK)
                {
                    hr = myCOMObject->Method4();
                }
                else
                {
                    hr = myCOMObject->Method5();
                }
            }
        }
        return hr;
    }

    In this really trivial example, it's vaguely clear what's going on, but it suffices.  One common change is to move the check for hr outside and repeatedly check it for each of the statements, something like:

        hr = myCOMObject->Method1();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method2();
        }
        if (hr == S_OK)
     

    What happens when you try that alternative implementation?

    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method2();
        }
        if (hr == S_OK)
        {
            hr = myCOMObject->Method3();
            if (hr == S_OK)
            {
                hr = myCOMObject->Method4();
            }
            else
            {
                hr = myCOMObject->Method5();
            }
        }
        return hr;
    }

    Hmm.  That's not as nice - some of it's been cleaned up, but the Method4/Method5 check still requires that you indent an extra level.

    Now consider what happens if you can use gotos:

    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        if (hr != S_OK)
        {
            goto Error;
        }
        hr = myCOMObject->Method2();
        if (hr != S_OK)
        {
            goto Error;
        }
        hr = myCOMObject->Method3();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method4();
        }
        else
        {
            hr = myCOMObject->Method5();
        }
        if (hr != S_OK)
        {
            goto Error;
        }
    Cleanup:
        return hr;
    Error:
        goto Cleanup;
    }

    If you don't like seeing all those gotos, you can use a macro to hide them:

    #define IF_FAILED_JUMP(hr, tag) if ((hr) != S_OK) goto tag
    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        IF_FAILED_JUMP(hr, Error);

        hr = myCOMObject->Method2();
        IF_FAILED_JUMP(hr, Error);

        hr = myCOMObject->Method3();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method4();
            IF_FAILED_JUMP(hr, Error);
        }
        else
        {
            hr = myCOMObject->Method5();
            IF_FAILED_JUMP(hr, Error);
        }

    Cleanup:
        return hr;
    Error:
        goto Cleanup;
    }

    Again, there are no right answers or wrong answers, just choices.

    Tomorrow, wrapping it all up.

  • Larry Osterman's WebLog

    What comes after Quaternary?

    • 21 Comments

    Valorie asked me this question today, and I figured I'd toss it out to everyone who runs across this post.

    She works in a 5/6 split class, and they're working on a unit on patterns and functions.  They're ordering the data into columns, each of which is derived from the information in the previous column.

    The question is: What do they label the columns?

    The first couple are obvious: Primary, and Secondary.

    Third and fourth: Tertiary, Quaternary.

    But what's the label for the fifth and subsequent columns?

    AskOsford.com suggested that they use quinary (5), senary (6), septenary (7), octonary (8), nonary (9) and denary (10), using the latin roots.

    But the teacher in the class remembers a different order and thinks that the next one (5) should be cinquainary (using the same root as the poetry form cinquains).

    Valorie also pointed to  http://mathforum.com/dr.math/faq/faq.polygon.names.html for a 2-page history lesson. Coolest fact she found: the "gon" part of the word means "knee" and the "hedron" means "seats" so a polygon means "many knees" and polyhedra means "many seats".

    So does anyone have any suggestions?

     

  • Larry Osterman's WebLog

    What does Larry's style look like?

    • 31 Comments

    Sorry this took so long - work's been really busy lately.

    Clearly I'm not enough of an egotist[1]  to believe that my style is in any way the "one true style", but over the course of writing the series on style, I realized that I've never written down my current coding style, and I think it would be vaguely humorous to write down "Larry's Coding Conventions".

    So here goes.  I'm using the keywords defined in RFC2119 within.

    Larry's Coding Conventions

    1. Files - Global file information.

    All source files (.C, .CXX, .CPP, etc) MUST be plain text files, with CR/LF (0x0D0x0A) at the end of each line.  Each file MUST end with a CRLF.  C++ code SHOULD be in files with an extension of .CPP, C code SHOULD be in files with an extension of .C.  Header files SHOULD have an extension of .H.

    Tab size MUST be set to 4, and tab characters MUST NOT appear in source files, this allows for a user to use any editor and still have the same experience while viewing the code.

    Every source file MUST start with the following header:

    /*++
     * <Copyright Notice (talk to your legal department for the format of the copyright notice)>
     *
     * Module-Name:
     *     <file name>
     *
     * Author:
     *     <author full name> (<author email address>) <date of creation>
     *
     * Abstract:
     *     <Brief abstract of the source file>
     *
     * Revision History:
     *
     *--*/


    Filenames SHOULD be representative of the contents of the files.  There SHOULD be one class (or set of functionality) per file.  So the CFoo class should be located in a source file named "Foo.cpp".

    Global variables SHOULD begin with a prefix of g_.  Care MUST be taken when declaring global variables, since they are likely sources of synchronization issues.

    Source code lines SHOULD be no longer than 100 characters long.

    2. Class definitions

    All classes SHOULD start with C in the class name.  Member variables of classes MUST start with an _ character.   Member variables are PascalCased (so a member variable could be _MyMemberVariable).

    Classes that are reference counted SHOULD follow the OLE conventions - an AddRef() and a Release() method should be used for reference counting.  If a class is reference counted, then the destructor for that class SHOULD be private.

    3. Functions and Methods - Info pertaining to various functions.

    Each routine MUST have a function header, with the following format:

    /*++
     * Function Name
     *
     *     <Description of the function>
     *
     * Inputs:
     *     <Description of the parameters to the function, or "None.">
     *
     * Returns:
     *     <Description of the return value, or "None.">
     *
     * Remarks:
     *     <Relevant information about the function, may be empty>
     *
     *--*/
     

    Function names SHOULD be representative of their function.  All function names MUST be in PascalCase as per the CLR coding standards.  If the project is using an auto-doc tool, it's acceptable to tag the inputs closer to their definition.

    Parameters to functions are also in PascalCase (note that this is a difference from the CLR coding standard).

    Local variables in functions should be camelCased.  This allows for the reader to determine the difference between local variables, parameters and class members easily.

    Parameter names SHOULD NOT match the names of methods in the class.

    Code SHOULD have liberal use of vertical whitespace, with descriptive block comments every five or so lines of source code.

    Descriptive comments SHOULD have the format:

        :
        :
        //
        //<space><space>Descriptive Comment Line 1
        //<space><space>Descriptive Comment Line 2
        //
        :
        :

    Each descriptive comment starts 4 spaces from the left margin, there is a single empty comment line before and after the descriptive comment, and two spaces between the // and the start of the comment text.

    Functions SHOULD occupy no more than one screen, or about 70 lines, including comments (not including headers).  This means that each function SHOULD be at most about 40 lines of code.

    4. Predefined Identifiers (Manifest Constants)

    Manifest constants SHOULD be in all upper-case.  Instead of using #define, enum's or const's SHOULD be used when possible, especially if the value being defined is unique, since it allows for better representation in the debugger (yeah, I know I've said that source level debuggers are a crutch, but...).

    5. Code layout

    Code is laid out using BSD-style - braces appear on their own line at the same indentation level as the conditional, code is indented 4 spaces on the line after the brace.

    So an if/else statement is formatted as:

        if (i < 0)
        {
            i += 1;
        }
        else
        {
            i += 1;
        }

    In general, unless semantically necessary, I use <variable> += 1 instead of <variable>++ or ++<variable>.

    Variable declarations SHOULD appear each on their own line.

    6. Code Example

    The following is an example of code in "Larry's Style".

    /*++
     * ErrorPrint
     *
     *     Print a formatted error string on the debug console.
     *
     * Inputs:
     *     Format - printf style format specifier
     *
     * Returns:
     *     None.
     *
     * Remarks:
     *     The total printf string should be less than DEBUG_STRING_BUFFER_SIZE bytes.
     *
     *--*/
    static void ErrorPrint(LPCSTR Format, ...)
    {
        int result = 0;
        static char outputBuffer[DEBUG_STRING_BUFFER_SIZE];
        va_list marker;

        va_start(marker, Format);
        result = StringCchVPrintfA(outputBuffer, DEBUG_STRING_BUFFER_SIZE, Format, marker);
        if (result == S_OK)
        {
            OutputDebugStringA(buffer);
        }
        va_end(marker);
    }

     

    [1] Ok, I've got a blog, that makes me an egotist, but not enough of an egotist[2]
    [2] Apologies to KC for stealing her footnoting style :)

    Edit: pszFormat->Format.

     

  • Larry Osterman's WebLog

    What does style look like, part 8 (the end)

    • 20 Comments

    So over the course of the past week, I've spent some time talking about various things that make up a program's "style". 

    There's one final aspect of programming style that I want to touch upon, and that's "Literate Programming".  Literate programming got its start with Donald Knuth's seminal book "Literate Programming".  The entire TeX project was written (or rather re-written) using literate programming.  Literate programming is in many ways the antithesis of many of the things I've talked about here - with a more traditional programming style, the code is the documentation, in literate programming, the documentation is the code.  The literate programming paradigm is a totally document-centric paradigm, in fact the code is almost irrelevant.  In the literate programming paradigm, your source code is a document, and the actually resulting program is built up by stitching together fragments of code.

    When I wrote my compiler back in my senior year in college, I wanted to explore the idea, so I wrote my compiler using literate programming.  Here's a simple example:

    58. Determine if the fields of a record are legit, and return a pointer to the type field of the rightmost field on the list Variable^.LHSOperand.

    <Check and return the type of a record field access 58> ==
      with  Variable^ do
          begin
          TSTE := Type_of_Var(LHSOperand);
         
    if LHSOperand^.NodeT in [Terminal, ProCall] then
             
    RValue := Type_of_Record(IDList, TSTE)
          else
              begin
             
    SemError( 'Huh? Record fields can only be identifiers or arrays', Error);
              UnParser(TTY, Variable);
              end;
        
    return;
         end

    This code is used in section 57.

    Section 57 had:

      if  Variable^.NodeT == LValue then
          <
    Check and return the type of a record field access 58>;
      if ....

    Literate programming isn't a style for everyone (I'm sure that the literate programming people are going to be mad at that statement), going back over my compiler, in retrospect, it's clear that I didn't do a great job of living up to the paradigm, but it IS a viable form of programming style.

     

    As I've pointed out, there are literally dozens and dozens of ways of writing a particular piece of code, and all of them are "correct".  The differences between those representations is what brings "style" to a piece of code.

    And just like fashion, coding styles have had trends - various coding constructs come in and out of style - for instance, currently exception handling is in, and error codes are out.  This may (and likely will) change as time goes on - time changes peoples perceptions of the value of various constructs (there was a time when structured error codes were the rage - that particular fad climaxed in x.500 error codes, which were highly structured entities that encapsulated not only the error, but the reason, recommended action, etc.

    Over time, every developer comes up with their own particular style - it may be the style that their mentor (or team) used, it may be the style that was in a piece of code they admired, it may be in a construct that they ran across in a textbook, it might just be from hard experience.

    Ultimately, the purpose of having a coding style is to result in more maintainable code.  Even if your code will only ever be used by you, having a consistent style is STILL critical - you're still going to have to maintain your code, and if your code is internally inconsistent, then you're going to struggle when you go back to that old code.

    I've got to say that I was reasonably pleased when I went back and looked at my old code for this series - there were some seriously cringe-worthy bits, and the quantity of comments was scarcer than it is today, but overall, it wasn't that hideous.

    If you're working on a team, it's even more important that you have a single coding style (or rather coding conventions).  As I pointed in this article last week, if you don't have a single coherent coding style, all you get is a mishmash, and mishmashes are rarely maintainable (and yeah, the spelling checker had mishmash in it).

    Coding style brings together individual and team discipline, personal esthetics, and a little bit of magic to make a coherent whole.

    Tomorrow, what does my personal coding style look like?

  • Larry Osterman's WebLog

    Coding with Style

    • 30 Comments

    AKA, Software archaeology, a working example.

    I've touched on this this a couple of times before, but I'd like to talk a bit about style.

    This isn't an issue for people working on a single person project, but once you start working in a team, the issue of coding style comes up often.

    The problem is that everyone has their own style, and they're usually pretty different.  K&R lays out several different styles, and Kernighan and Plaugher lay out still more.  Style encompasses everything from where the braces go, to how far they're indented (and where they're indented), to the names of identifiers (both variables and function names), to where the variables are declared.  Even if source files contain spaces or tabs (and what the tab setting is) is an aspect of programming style.

    Most programmers learn a particular style when they start to learn how to program, and then they tend to stick with that style for their career (with minor modifications).  One thing to keep in mind about style is that it's personal.  Nobody's style is any better than anyone else's style, they're all ultimately a matter of personal choice (having said that, some personal style choices can be rather distracting, for a simple example, see this Daily WTF post).

    When you're laying out a project at the beginning, you essentially have two ways to go when determining the style for your code.  The first is to get all the developers together in a meeting, hash out the details, and write a specification for the coding style for your project.  And then you've got to be diligent in enforcing that style, especially if the style you're enforcing is different from that of an individual developer in your group.

    The other way is to be somewhat more ad-hoc in coding style - typically each developer owns one or more components in the system, you let them develop those components with their own style, and trust that the final product will be harmonious.  If you're using paired programming, or if there is more than one owner of a piece of software, then clearly the individual developers need to come to consensus on their style.

    There are times that the ad-hoc coding style is effectively forced on an organization.  This happens a lot when dealing with legacy code - often times the code was written by developers who have long left the organization, and thus they are no longer maintaining the code.

    There's one critical thing to deal with when you're dealing with disparate styles - you should never, ever change existing code to match your personal style.  I've seen this happen in real life, two developers on a team each have their own idea of what a variable should be named, and as the two developers work on the module, each of them goes through and makes sweeping changes to rename the variables to meet their personal naming convention.  The biggest problem with this is that sweeping changes like this cause huge issues with code reviews - each gratuitous change to the code shows up as a change when diff'ing source files, and they increase the signal-to-noise ratio, which reduces the effectiveness of the code review - the code reviewer can easily miss a critical error if it's buried deep in a sea of variable name changes.

    For those of you who have worked on long running projects, how many times have you run across code like this?

    typedef int CB;
    typedef wchar * SZ;
    #define length 20
    void MyFunctionName(SZ szInput, CB cbInput)
    {
        CB stringlength = strlen(szInput);
        char myBuffer[length];
        if (stringlength < length)
        {
            if (szInput[0] == 'A')
                {
                    <Handle the case where the input string starts with 'A'>
                }
            else {
            if ('B' == szInput[0]) {
                <Handle the case where the input string starts with 'B'>
            }
            }
        }
    }

    What happened here?  Well, the code was worked on by four different developers, each of whom had their own style, and who applied it to the source.  The one that authored the routine used strict Hungarian - they defined types for SZ and CB, and strictly typed the parameters to the routine.  The next developer came along, and renamed the local variables to match their own personal style, and added the check for case "A".  The second developer used the following style for their if/then statements:

    if (if condition, constants in conditionals appear on the right)
        {
            <if statement>
        }
    else
        {
            <else statement>
        }

    Now, a 3rd developer came along, this developer added the check for case "B".  The third developer used the following style for their if/then statements:

    if (if condition, constants in conditionals appear on the left) {
        <if statement>
    }
    else {
        <else statement>
    }

    And then, finally the 4th developer came along and added the buffer overflow case.  Their if/then style was:

    if (if condition, constants in conditionals appear on the left)
    {
        <if statement>
    }
    else
    {
        <else statement>
    }

    None of these styles is wrong, they're just different.  But when put together, they turn into a totally unmaintainable mess.

    So it's utterly critical, if you're working on a project that uses ad-hoc coding standards that you adopt your coding style to match the code.  The future maintainers of the code will thank you for it.  Even if your project has strict coding standards, the instant you have to deal with legacy code, you MUST adopt the coding standard of the original author of the code, even if it doesn't match your groups standards.

  • Larry Osterman's WebLog

    What does style look like?

    • 17 Comments
    Building on my post on style last week.

    So what does "coding style" look like, anyway?

    Well, the facile answer is "I know it when I see it".  But it's more than that.  A good coding style should result in high quality self-documented code that is esthetically pleasing to look at.  This is not to reduce the value of external specifications, they are utterly critical to the success of a project, but external specifications serve a different purpose from the code.  Neither can function independently.

    I thought it might be interesting to see what happens to a single piece of code when you use different styles on it.

    For the purposes of illustration, I opened my copy of Robert Sedgewick's "Algorithms in C, 3rd edition" to a random page, and picked the most reasonable example from that page.  On that page is the following code:

    #include "list.h"
    main(int argc, char *argv[])
      {
        int i, N = atoi(argv[1]), M = atoi(argv[2]);
        Node t, x; initNodes(N);
        for (i = 2, x = newNode(1); i <= N; i++)
          { t = newNode(i); insertNext(x, t); x = t; }
        while (x != Next(x))
          {
            for (i = 1; i < M ; i++) x = Next(x);
            freeNode(deleteNext(x));
          }
        printf("%d\n", Item(x));
      }

    This is the code as it exists in the book, any errors come from my poor transcription efforts.

    Let's see what happens to the code if you apply a couple of different coding styles.  Please note: there's no one-true coding style.  They're all different, and they all have their advantages.

    First up, Hungarian.  Hungarian's a structure-neutral coding style, so all I did was to hungarian-ize the variables and functions, without touching the other aspects.

    #include "list.h"
    main(C cArg, SZ rgszArg[])
      {
        I iNode, cNodes = atoi(rgszArg[1]), cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT, pnodeCur; InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
          { pnodeT = PnodeNew(i); 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));
      }

    So what changed?  First off, all the built-in types are gone.  Hungarian can use them, but not for most reasons.  Next, the hungarian types "I", "C", and "SZ" are used to replace indexes, counts and strings.  Obviously the C runtime library functions remain the same.  Next, I applied the appropriate prefix - i for indices, c for counts, p<type> for "pointer to <type>".  The Node type was renamed PNODE, in Hungarian, all types are uppercased.  In Hungarian, the name of the routine describes the return value - so a routine that returns a "pointer to foo" is named "Pfoo<something relevent to which pfoo is being returned>".

    Looking at the transformation, I'm not sure it's made the code any easier to read, or more maintainable.  The next examples will try to improve things.

  • Larry Osterman's WebLog

    Hey wow, I'm famous :)

    • 23 Comments

    Wow, what an amazing evening.

    Earlier this evening, Valorie and I had the opportunity to attend my 20th anniversary dinner at the Newport Hills Golf Club (this place has the most unbelievable views of Seattle, Bellevue, and the Olympics).

    We got to hang out with a bunch of other people who've been at Microsoft for 20 years, and the evening was capped by a really awesome speech by Bill (it was sort-of an interactive speach, since we kept on interrupting him to call out details of things that happened 20 years ago).  Even Valorie had a good time (it turns out that she knew more people who were there than I did :)).

    And then I got home, and started reading my email, and ran across a comment indicating that Robert Scoble just posted the first part of an interview that I did for Channel 9 last month!  Now the beard behind the words gets shown in public.  Ya know, you don't really realize your habits until you see them on video.

    A perfect capper on a great evening.

    Edit: Larry needs a spell checker.

     

  • Larry Osterman's WebLog

    What does style look like, part 3

    • 25 Comments

    When we last left the code we were looking at, it looked like:

    #include "list.h"
    main(C cArg, SZ rgszArg[]) {
        I iNode, cNodes = atoi(rgszArg[1]), cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT, pnodeCur;
        InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++) {
            pnodeT = PnodeNew(i);
            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));
    }

    This time, let's consider a different indentation style:  Braces after the expression, every optional brace included (modified BSD)

    #include "list.h"
    main(C cArg, SZ rgszArg[])
    {
        I iNode, cNodes = atoi(rgszArg[1]), cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT, pnodeCur;
        InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
        {
            pnodeT = PnodeNew(i);
            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));
    }

    That's a fascinating difference.  Moving the braces down to the next line opens the code up quite a bit, making it easier to see how the code is structured.  Next, what happens with the declarations - the declaration of iNode,cNodes, and cNodesToSkip is smushed together, lets split them out into separate lines.

    #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(i);
            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));
    }

    Again, this helps a bit, but not a lot.  What about other bracing styles?

    #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(i);
                    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));
    }

    8 character indents stretch the code out a bunch, but they don't effect the overall effect.  How about a variant of 8 char indents?

    #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(i);
                    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));
        }

    This one's kinda interesting.  The braces are now more clearly laid out.  On the other hand, it's a bit disconcerting to have the "printf" hanging out on its own.  Personally, I'm not too happy with this one.

    How about the "braces appear on the same line as the code they wrap"?

    #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(i);
            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));
    }

    An interesting variant.  I don't know if it's an improvement, but...

    Just for grins, the style of a senior developer within Microsoft:

    #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(i);
            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));
        }

    In this style, the opening brace goes with the if/while/for loop, but the closing brace goes with the code at that level of indentation.  Again, it's an "interesting" choice.

    Tomorrow, we add comments.  That'll help a bundle towards helping this out.

     

  • Larry Osterman's WebLog

    What does style look like, part 6

    • 14 Comments
    Previously in the series, I've touched on indentation and commenting, The next aspect of "style" that I want to talk about is variable (and function) naming conventions.

    Upon reflection, I hungarianized the code way too early in the series, it properly belongs in this article, so lets go back to the beginning, and see what happens to the code with different naming styles.  Here's the original code:

    #include "list.h"
    main(int argc, char *argv[])
      {
        int i, N = atoi(argv[1]), M = atoi(argv[2]);
        Node t, x; initNodes(N);
        for (i = 2, x = newNode(1); i <= N; i++)
          { t = newNode(i); insertNext(x, t); x = t; }
        while (x != Next(x))
          {
            for (i = 1; i < M ; i++) x = Next(x);
            freeNode(deleteNext(x));
          }
        printf("%d\n", Item(x));
      }

     

    Rewriting the code in hungarian yields:

    #include "list.h"
    main(C cArg, SZ rgszArg[])
      {
        I iNode, cNodes = atoi(rgszArg[1]), cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT, pnodeCur; InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
          { pnodeT = PnodeNew(i); 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));
      }

    So what changed?  First off, all the built-in types are gone.  Hungarian can use them, but not for most reasons.  Next, the hungarian types "I", "C", and "SZ" are used to replace indexes, counts and strings.  Obviously the C runtime library functions remain the same.  Next, I applied the appropriate prefix - i for indices, c for counts, p<type> for "pointer to <type>".  The Node type was renamed PNODE, in Hungarian, all types are uppercased.  In Hungarian, the name of the routine describes the return value - so a routine that returns a "pointer to foo" is named "Pfoo<something relevent to which pfoo is being returned>".

    But, of course, there are LOTs of alternative naming conventions other than Hungarian.  For example, Microsoft's published extensive documentation on design guidelines for class library developers who want to publish managed libraries.  Lets recast the sample application in the .Net coding style:

    So what does the .Net/CLR coding style look like?  First off, the CLR naming guidelines for methods require that they use PascalCase, and that verbs or verb phrases are used to name the methods.  Second, the CLR naming guidelines require that parameters to functions obey camelCase.  This example doesn't have any parameters declared other than argv and argc, so that aspect dosn't change.  But the naming guidelines are pretty clear about the choice of names: parameters should always be spelled out, abbreviations should only be used if developers generally understand them.  And you should NOT use hungarian notation.  They go on to say that "good names describe semantics, not type".  I find this last one fascinating, since it highlights one of the major differences between systems Hungarian and applications Hungarian - Systems Hungarian represents the type, apps Hungarian represents the semantics.

    Because the CLR naming conventions don't describe anything about local variables, the CLR standards for local variables have to be inferred.  This makes sense because the CLR coding standards are all about describing the public interfaces to components, and not their internal standards.  As such, they explicitly don't cover local variables (or member variables (fields), except to say that you're not allowed to have any).  But a CLR-ish style can be inferred.  The first thing that's clear is that abbreviations are out, and that variables should represent full English words.  I'm going to go out on a limb and keep the "i" as an indexer because it's a convention that's pretty standardized.  This still leaves the camelCase vs PascalCase issue open, just for grins, lets go with camelCase. 

    #include "list.h"
    main(int argCount, char *args[])
      {
        int i,  numberOfNodes = atoi(args[1]), nodesToSkip = atoi(args[2]);
        Node newNode, currentNode; InitNodes(numberOfNodes);
        for (i = 2, currentNode = NewNode(1); i <= numberOfNodes; i++)
          { newNode = NewNode(i); InsertNext(currentNode, newNode); currentNode = newNode; }
        while (newNode != Next(currentNode))
          {
            for (i = 1; i < nodesToSkip ; i++) currentNode = Next(currentNode);
            FreeNode(DeleteNextNode(currentNode));
          }
        printf("%d\n", Item(currentNode));
      }

    One thing that was pointed out by one of my co-workers is that it's often extremely useful to visually differentiate between parameters, local variables, and member variables, and in this example you can't differentiate between parameters and locals.  This isn't as big a deal in a small example like this, but for a larger, more complicated example, it might turn into a big deal.

    If we decide that parameters are declared in camelCase, if you apply this paradigm, you can name local variables with PascalCase.  That leaves member variables.  Even though the CLR guidelines for members explicitly prevents prefix characters (like m_ and g_), if you just use "_" as a prefix character, it allows you the ability to differentiate between them.

    Lets look at the example using camelCase for parameters and PascalCase for local variables.

    #include "list.h"
    main(int argCount, char *args[])
      {
        int I,  NumberOfNodes = atoi(args[1]), NodesToSkip = atoi(args[2]);
        Node NewNode, CurrentNode; InitNodes(NumberOfNodes);
        for (I = 2, CurrentNode = NewNode(1); I <= NumberOfNodes; I++)
          { NewNode = NewNode(i); InsertNext(CurrentNode, NewNode); CurrentNode = NewNode; }
        while (NewNode != Next(CurrentNode))
          {
            for (I = 1; I < NodesToSkip ; I++) CurrentNode = Next(CurrentNode);
            FreeNode(DeleteNextNode(CurrentNode));
          }
        printf("%d\n", Item(CurrentNode));
      }

    Hmm. Two major issues jump out immediately with this example.  The first one is:

    NewNode = NewNode(i);

    There's a major inconsistency here - the local variable and the function have exactly the same name (of course, in a non case-sensitive language, this issue is more significant).  With luck, the ambiguity can be resolved by the compiler, but that's only for this case - and a style has to work for ALL cases (pun unintended).  It's pretty horrible when you get to the point when you realize that youve got to either (a) change your code for the sake of style of (b) change your style for the sake of the compiler.

    This illustrates an important aspect of a naming convention: Whatever you do, you shouldn't have a naming convention that allows for ambiguity.  In this case, each naming (and capitalization) convention can be considered as a puzzle piece.  When you come up with your final coding style, you need to take all of these into account.  You have four variables: Global Function/Member Function naming convention/case, Parameter naming convention/case, Local Variable naming convention/case, and Member Variable naming convention/case.  And your have a myriad of possible naming convention/case options, from hungarian to CLR, from PascalCase, to camelCase, to whatever else you can come up with.

    When defining a style, you need to balance all of these aspects to come up with a coherent whole.  Fundamentally, it doesn't matter which naming/case convention you chose for each of the variables, just as long as it's aesthetically pleasing to you and your team.

    So lets take the sample application and reframe it in the first of the CLR conventions I mentioned above (camelCase local variables):

    /*++
     *        <Copyright Notice>
     *
     *  Module Name:
     *        MyProgram.c
     *
     *  Abstract:
     *        This program implements a solution to the Josephus problem. 
     *        Code taken from Robert Sedgewick's "Algorithms in C, Third Edition",
     *        Program 3.13, on Page 103.
     *
     *  Author:
     *        Robert Sedgewick
     *
     *  Revision History:
     *
     *    LarryO             11/12/2004     Restructured for weblog posts.
     *    Robert Sedgewick   <Unknown>      Created. 
     --*/

    #include "list.h"


    /*++    main - Implements the Josephus problem and prints the result.
     *
     *
     *     main (cArg, rgszArg)
     *
     *     ENTRY   cArg - number of arguments.
     *             rgszArg - array of cArg null terminated strings
     *     EXIT    None.
     *
     *     <discussion of internals, one or more paragraphs>
     *
     *
     --*/
    main(int argCount, char *args[])
    {
        int i
        int numberOfNodes = atoi(args[1]);
        int nodesToSkip = atoi(args[2]);
        Node newNode;
        Node currentNode;

        InitNodes(numberOfNodes);

        //
        // Create a list of cNodes nodes. 
        // 
        for (i = 2, currentNode = NewNode(1); i <= numberOfNodes; i++)
        {
            newNode = NewNode(i);
            InsertNext(currentNode, newNode);
            currentNode = newNode;
        }

        //
        // Walk the list of nodes, freeing the node that occurs at every cNodesToSkip nodes in the list.
        //
        while (newNode != Next(currentNode))
        {
            for (i = 1; i < nodesToSkip ; i++)
            {
                currentNode = Next(currentNode);
            }
            FreeNode(DeleteNextNode(currentNode));
        }


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

    When I first rewrote the code in hungarian, I got screams from the commenters about how hideous it looked in hungarian. Just for grins, lets compare the code portions of the two examples (hungarian and CLR).

    First the hungarian:

    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));
    }

    Now, the CLR:

    main(int argCount, char *args[])
    {
        int i;
        int numberOfNodes = atoi(args[1]);
        int nodesToSkip = atoi(args[2]);
        Node newNode;
        Node currentNode;

        InitNodes(numberOfNodes);

        //
        // Create a list of numberOfNodes nodes. 
        // 
        for (i = 2, currentNode = NewNode(1); i <= numberOfNodes; i++)
        {
            newNode = NewNode(i);
            InsertNext(currentNode, newNode);
            currentNode = newNode;
        }

        //
        // Walk the list of nodes, freeing the node that occurs at every cNodesToSkip nodes in the list.
        //
        while (currentNode!= Next(currentNode))
        {
            for (i = 1; i < nodesToSkip ; i++)
            {
                currentNode = Next(currentNode);
            }
            FreeNode(DeleteNextNode(currentNode));
        }


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

    What I find fascinating about putting these two examples side-by-side is that stylistically, there's actually not that much difference between the two - if you hold all things constant and just vary the naming convention, the code isn't that different.

    Tomorrow, lets look at some C/C++ minutae.

     

    EDIT: Fixed some typos (Thanks David Mellis).

     

  • Larry Osterman's WebLog

    What does style look like, part 5

    • 14 Comments
    Continuing the discussion of "style"..

    The other day, I talked a bit about how comments affect the "style" of a piece of code.

    Today, I want to talk  about the headers that accompany each routine and file.

    And yes, every routine and file needs to have a block comment header around it.  If you don't , then the next person who gets to maintain the file curses you.

    At a minimum, the file header should have a brief summary of what the code in the file does, and probably a copyright notice.

    For subroutines, the name of the routine and a brief (or maybe not-so-brief) description of what it does.

    Beyond that, anything goes.  I've seen literally dozens of variants of styles.  Some of my oldest code has the following for routine headers (written in Bliss36, so ! is a comment character):

    ! Copyright (C) 1982, Lawrence Osterman
    ! CHANGE LOG
    !-------------------------------------------------------------------
    ! 9 Nov 82 OSTERMAN Added a check to see if you are watching
    !                   a user when you auto add them.

    Each routine had:
    !++
    ! Get_Accounts_JFN :
    !
    ! Input:
    !    None
    ! Output:
    !    None
    ! Side effects:
    !    None
    !
    !--
     

    Not the cleanest, but... 

    Unfortunately, I don't have any good examples of a module header, typically one looks like:

    /*++
     *        <Copyright Notice>
     *
     *  Module Name:
     *        <Name of module filename>
     *
     *  Abstract:
     *        <Brief abstract for module>
     *
     *  Author:
     *        <Authors name and email>
     *
     *  Environment:
     *        <intended environment for this module (optional)>
     *
     *  Notes:
     *        <optional notes>
     *
     *  Revision History:
     *
     *    <Author> <Change Date> <Change Description>
     --*/
     

    The "Author" field can be controversial - Since modules can change owners, it's not clear if there's long-term value for the owner field.  Similarly, the revision history is controversial.  It can be extraordinarily useful, but only if maintained diligently.

    My earliest copy of Microsoft documentation (from 1984) has the following example of a subroutine header:

    /***    name - very brief description
     *
     *     <description>
     *
     *     name    (parm1, parm2, parm3, ..., parmn)
     *
     *     ENTRY   parm1 - description
     *             parm2 - description
     *     EXIT    parm3 - description
     *             .
     *             parmn - description
     *
     *     <discussion of internals, one or more paragraphs>
     *
     *     WARNING:    <discussion of limitations, gotchas, etc.>
     *
     *     EFFECTS:    <'none', or discussion of global effects>
     *
     */

    Again, this was 1984, but in general, the concepts involved are the same.  This block has all of the requirements for the routine header included.  Beyond that, it's all esthetics.

    Here are some styles I've seen (building on the early coding standard above).

    /*++    name - very brief description
     *
     *     <description>
     *
     *     name    (parm1, parm2, parm3, ..., parmn)
     *
     *     ENTRY   parm1 - description
     *             parm2 - description
     *     EXIT    parm3 - description
     *             .
     *             parmn - description
     *
     *     <discussion of internals, one or more paragraphs>
     *
     *     WARNING:    <discussion of limitations, gotchas, etc.>
     *
     *     EFFECTS:    <'none', or discussion of global effects>
     *
     --*/

    As I said, this is all about esthetics, so everyone has their own favorites.

    /********************************************************************\
    *                                                                    *
    *    name - very brief description                                   *
    *                                                                    *
    *     <description>                                                  *
    *                                                                    *
    *     name    (parm1, parm2, parm3, ..., parmn)                      *
    *                                                                    *
    *     ENTRY   parm1 - description                                    *
    *             parm2 - description                                    *
    *     EXIT    parm3 - description                                    *
    *             .                                                      *
    *             parmn - description                                    *
    *                                                                    *
    *     <discussion of internals, one or more paragraphs>              *
    *                                                                    *
    *     WARNING:    <discussion of limitations, gotchas, etc.>         *
    *                                                                    *
    *     EFFECTS:    <'none', or discussion of global effects>          *
    *                                                                    *
    \********************************************************************/

    This one's hard to maintain, because of the trailing *'s, but it is pretty.  It might do for a file header.

    And of course, there's the version with just C++ comments:

    //-----------------
    //    name - very brief description
    //
    //     <description>
    //
    //     name    (parm1, parm2, parm3, ..., parmn)
    //
    //     ENTRY   parm1 - description
    //             parm2 - description
    //     EXIT    parm3 - description
    //             .
    //             parmn - description
    //
    //     <discussion of internals, one or more paragraphs>
    //
    //     WARNING:    <discussion of limitations, gotchas, etc.>
    //
    //     EFFECTS:    <'none', or discussion of global effects>
    //
    //---

    In general, if I had to come up with rules for what makes subroutine headers work, I'd say that they had to be descriptive, they had to include documentation for all the parameters, they had to have some consistent style to differentiate them from normal code.

    One other thing that effects subroutine headers is the existance of tools like AutoDOC.  If you choose to use an auto-documentation tool like autodoc (and there are dozens of them), then your subroutine headers can form the basis of the internal documentation for your module.  It doesn't change the fact that you've got to have design specifications, but it does provide an external reference tool.

    So lets go back to the commented code and add some header and routine comments:

    /*++
     *        <Copyright Notice>
     *
     *  Module Name:
     *        MyProgram.c
     *
     *  Abstract:
     *        This program implements a solution to the Josephus problem. 
     *        Code taken from Robert Sedgewick's "Algorithms in C, Third Edition",
     *        Program 3.13, on Page 103.
     *
     *  Author:
     *        Robert Sedgewick
     *
     *  Revision History:
     *
     *    LarryO             11/12/2004     Restructured for weblog posts.
     *    Robert Sedgewick   <Unknown>      Created. 
     --*/

    #include "list.h"


    /*++    main - Implements the Josephus problem and prints the result.
     *
     *
     *     main (cArg, rgszArg)
     *
     *     ENTRY   cArg - number of arguments.
     *             rgszArg - array of cArg null terminated strings
     *     EXIT    None.
     *
     *     <discussion of internals, one or more paragraphs>
     *
     *
     --*/
    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));
    }

    That's quite different.  Tomorrow, we start looking at naming conventions.

     

  • Larry Osterman's WebLog

    November 2nd

    • 27 Comments

    Twenty four years ago, on the Tuesday of the first full week in November, I dutifully filled in my absentee presidential ballot (for John Anderson, who was running as an independent).

    I've voted in every election that I've been eligible since then.

    As a citizen of a democracy, it is my civic responsibility to vote.  And I take that responsibility very seriously.  I vote in primaries, I vote in special elections, I vote in general elections.

    This year it's especially important to vote, regardless of whose side you support, your candidate needs your support.  It's critical that EVERYONE who can vote, vote.

    This year, it looks like Washington State is going to have an 85% turnout of registered voters, which is likely to be the highest since WWII.  I am indescribably proud of this statistic (OTOH, in 2000, 74% of the registered voters voted, which was only 56% of the voting age population)

    Unfortunately, the turnouts in other states aren't nearly as good, for instance, in 2000, only 55% of the registered voters in Oklahoma turned out to vote (48% of the voting age population). 

    With an election that is this close, and with what appear to be concerted efforts to suppress the vote in close contests, it is even more critical that everyone take time off from work and vote.  As I said - I don't care who you vote for, just that you vote. 

    If you don't vote, then you don't get to complain.

    Valorie chased down the following poem by John Greenleaf Whittier that was read on NPR this morning.  It's a bit florid (it was written in 1848) but it says it well:

    THE POOR VOTER ON ELECTION DAY.

    THE proudest now is but my peer,
    The highest not more high;
    To-day, of all the weary year,
    A king of men am I.
    To-day, alike are great and small,
    The nameless and the known;
    My palace is the people's hall,
    The ballot-box my throne!

    Who serves to-day upon the list
    Beside the served shall stand;
    Alike the brown and wrinkled fist,
    The gloved and dainty hand!
    The rich is level with the poor,
    The weak is strong to-day;
    And sleekest broadcloth counts no more
    Than homespun frock of gray.

    To-day let pomp and vain pretence
    My stubborn right abide;
    I set a plain man's common sense
    Against the pedant's pride.
    To-day shall simple manhood try
    The strength of gold and land;
    The wide world has not wealth to buy
    The power in my right hand!

    While there's a grief to seek redress,
    Or balance to adjust,
    Where weighs our living manhood less
    Than Mammon's vilest dust,--
    While there's a right to need my vote,
    A wrong to sweep away,
    Up! clouted knee and ragged coat
    A man's a man to-day
    1848.

    Tomorrow, back to technical stuff.
  • Larry Osterman's WebLog

    What does style look like, part 2

    • 13 Comments

    In my previous style post, I took a piece of code from  Robert Sedgewicks algorithms book, and "Hungarian-ized" it.  The routine currently looks like:

    #include "list.h"
    main(C cArg, SZ rgszArg[])
      {
        I iNode, cNodes = atoi(rgszArg[1]), cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT, pnodeCur; InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
          { pnodeT = PnodeNew(i); 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));
      }

    Btw, I'll show what this code looks like without hungarian during this series, don't worry :).

    Now that it's hungarianized, lets look at the structure.  The first thing that stands out is that it's not completely consistent.  Every indentation is 2 spaces, but sometimes lines are compressed, sometimes not.  This is undoubtedly a concession to the space requirements in the book, it's not likely something you'll find in production code.

    Lets re-format the code and see what happens.  First, I'll use the "braces appear on the same line as the conditional, indented 4 characters (K&R)" style:

    #include "list.h"
    main(C cArg, SZ rgszArg[]) {
        I iNode, cNodes = atoi(rgszArg[1]), cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT, pnodeCur; InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++) {
            pnodeT = PnodeNew(i); 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));
    }

    To me, the code hasn't changed that much.  Lets make another change: Moving each statement to its own line:

    #include "list.h"
    main(C cArg, SZ rgszArg[]) {
        I iNode, cNodes = atoi(rgszArg[1]), cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT, pnodeCur;
        InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++) {
            pnodeT = PnodeNew(i);
            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));
    }

    That's somewhat better, the routine takes some more vertical space, but it's a bit clearer what's going on.

    Tomorrow, BSD style, and a bunch of other indentation varients.

     

  • Larry Osterman's WebLog

    I need to apologize

    • 14 Comments

    Sorry about leaving things hanging, I'm currently on vacation, that's why the 'blog's not been updated.

    I'll be back on Monday the 29th and continue where things left off.

     

  • Larry Osterman's WebLog

    I don't normally cross-post, but this was just too funny.

    • 8 Comments

    From TheDailyWTF:

    DBA:  Do you want usernames to be unique?
    Developer:  Yes

    (next day)

    DBA: Should passwords be unique as well?
    Developer:  No

    (later that day)

    DBA: Shouldn't we require passwords to be unique?
    Developer: No
    DBA:  Are you sure?
    Developer: <15 minutes of explanation as to why different users are not required to have different passwords, just usernames>

    (next day)

    DBA:  Your procedure doesn't return a specific error message.
    Developer:  It's a security issue, and should only return "Invalid Username/Password Combo."
    DBA:  So if they get the username wrong, but the password correct you're not going to display a specific message?

    Full text found here.

  • Larry Osterman's WebLog

    Another year, another halloween

    • 11 Comments

    Well, another Halloween has come and gone.  Last night was quite depressing, only one kid showed up at our door.

    On the other hand, the kids and I had a lot of fun carving pumpkins, for some reason we missed doing that last year, so it was nice to get back on track with that tradition.

    As is usual, the day of (or the last business day before) Halloween, we had hundreds and hundreds of tick-or-treaters at Microsoft however, it was HUGELY fun watching all those kids running around the hall.

    I love seeing the excitement as they run from office to office trying to find out what each person has in their goody stash.

    "Hey, he's got NERD ROPES!"

    "What are these things?  I've never heard of a candy called "Sonho de Valsa"?"

    One of the really cool things about working in such a diverse company is the variety of candy that people put out.  Most of the people do the Hershey Miniatures thing, but every once in a while, you run into someone who's gone to the trouble of getting candy from their native country.  So I've had the opportunity to see Russian candy, Ecuadorian candy, English candy, Japanese candy, Korean candy, etc.  It adds a great deal of spice to an otherwise monotonous candy basket.

    I know there are people who are seriously upset about Halloween, but IMHO, it's one of my favorite holidays.

    Btw, check out Adam Barr's pumpkin, he put the Channel9 guy on a pumpkin :)

     

    Edit: #$@@#$@#$@ Newsgator double encoding - I thought that was gone.

  • Larry Osterman's WebLog

    Third video's up

    • 4 Comments

    Robert just posted the 3rd video, it can be found here.

    Enjoy!

  • Larry Osterman's WebLog

    And the fourth video's now up on Channel9.

    • 10 Comments

    Robert just posted the fourth of the videos he shot last month.

    This one's short, about the hardest problem I've solved.

  • Larry Osterman's WebLog

    Still more videos posted

    • 5 Comments

    Robert just dropped the second of my Channel9 videos up, you can find it here.

    Enjoy :)

     

  • Larry Osterman's WebLog

    Fifth video's up

    • 0 Comments

    The fifth (and last) video's up here,  if you've been reading my blog, it's not new, but :)

    Sorry about no real content, work's been pretty busy this week.

Page 1 of 1 (21 items)