Welcome to MSDN Blogs Sign in | Join | Help

Why You'll Never See a Sample From Me...

I noticed yesterday that Peter and Patrick are trading comments and thoughts on coding styles.  So I'll come out of my cage and admit I am the consummate iconoclast on most issues of style.  When I review code, I make comments about logic, out-and-out errors, and (generally adding that I don't regard it as important) typos in comments.  Beyond that, I don't bother.  When I read the occasional forum thread on coding, I just sort of sit and shake my head as I read how much people hate the way I like to think and loathe the things I find useful to do.

But that's nothing new- I learned a long time ago that my ideas were different.  But I've spent my years in maintenance and other activities and still have them.

Most standards are fueled by someone's personal tastes being enforced by fiat on a team, usually backed up by anecdotal assertions about how useful they are in maintenance.  You take liberal amounts of those add a dash of geopolitical correctness or "professional presentation" reviews and a nice heaping of endless semantic analysis of names and what I once found to be enjoyable (writing code to make a computer solve a problem) becomes one of the most tedious tasks on earth.

I loathe standards.  I deliberately flout them at times just to get the pent-up frustrations off my chest.  I usually revert to using them [pragmatism again], but occasionally get caught when I write some toy that someone later decides should be kept but they don't want to rewrite it [hard to understand, because for the life of me, most devs secretly want to rewrite every line of code they see so it fits their personal style- usually they have to wait unitl they have a senior position, at which point they have learned to call it "refactoring", which makes it sound more worthwhile than "making it pretty because currently it iz teh suk".

Of late, I like this set of rules:

  • 132 columns: 80 columns- that was punched cards.  I used them, they sucked.  Character mode displays- same limit because they had to display what was on punched cards.  They were better, but I like my hi-res displays much better, thank you.  Printers- I wrote and tested printer drivers for over a decade.  I've personally destroyed enough trees I should rot in hell for several eternities.  I only print when I must, and then I use both sides, N-up and whatever else I can do to keep my repentant attitude.  And if some line gets longer than that, I'll fix it if I feel like it.
  • Mnemonic names are good.  CamelCasingRules.  Elaborate and contrived jokes are best, as long as you don't waste too much time on them.
  • Putting each parameter in a function call on a separate line is a waste of good screen real estate.  I learned how to read more than one word at a time years ago.  If I care what the parameters are, I'll look closer- I can even pop up a nice API description from MSDN or wherever if I need to (or use a class browser, etc).  If I don't care what they are, I like being able to ignore one or two lines of code instead of a dozen.
  • C++ overloading is useful.  That goes for functions, operators and even casts.  If a line of code looks confusing, I use a browser to tell me which if any of those are in play.  I then use intuition to tell me where the problems are likely to be.
  • My sole old-fashioned item is I adhere to Djikstra's seminal laws.  Gotos are unnecessary.  I could care if they look pretty- I still see more screwups today from their use than anything else.  But I also am pragmatist and observer of the human condition enough to know a pointless battle when I see it.  Each generation of programmers is smart enough to never do it wrong, and knows better than some dead Dutch guy how to write good code.  Just ask them, they'll tell you.  Global variables is the other one [at least, that's how I remember it].
  • C is a good language for dinosaurs.  Plod along, ye denizens of the past!

Next week, maybe I'll adopt a different set of attitudes, just to be ornery.  But I can only observe hours of discussion about a name or a standard like "do it this way because then when I use a proportional font I like the way it looks" or "all function names will begin with the camel-cased path to the source file from the base of the build tree [that's not a joke, BTW- I really worked where that was the standard- geez, use a PDB]".

SDETs aren't totally immune from such standards, but things are a bit more lax, and people maybe don't watch me as much as they should to keep me from misbehaving.  I think my most recent misdeed was creating a quick off-the cuff C++ class to wrap a buffer pointer so it wouldn't leak (similar to the RAII pattern).  Being in a hurry, I named the class the first thing that came to mind: leak free buffer = Pampers.  Ordinarily I would have gone back and changed it, but I got in a hurry and checked it in anyway.  I'm sure I'll get a diaperful about doing that at some point...

So just for fun, here are concrete examples of why you'll not be seeing samples from me:

From WdfVerifier:

/**********************************************************************************************************************************

void    ExplainHowTheCommandLineWorksInMindNumbingDetail()

Does as it says, says what it does.

**********************************************************************************************************************************/

void    ExplainHowTheCommandLineWorksInMindNumbingDetail()
{
    //  Like a good localizable application, the text for the message box is now in the resources.
    String  Explanation(IDS_Explanation);

    MessageBox(NULL, Explanation, ApplicationName, MB_ICONINFORMATION | MB_OK);
}
That happens to get called from here:
/**********************************************************************************************************************************

extern "C" int __cdecl main(int, __in char**)

Yes, I cheated- even though this is a Windows program, it still uses main- it uses the Windows API to get a unicode command line.
Seriously, this works fine, and it isn't any danger at all.  Used to do it all the time in the early days of NT (those halcyon days
when you had to start Windows from the command line after the system booted).

**********************************************************************************************************************************/

extern "C" int __cdecl main(int, __in char**)
{
    //  Check the command line- it will tell us if we need to display a UI or just function as a console app.

    int     ParameterCount = 0, ServiceCount = 0;
    bool    ExplainYourself = false;
    bool    RemoteAccessRequired;
    PWSTR   MachineName = NULL;
    PWSTR*  Parameters = CommandLineToArgvW(GetCommandLineW(), &ParameterCount);
    
    //  Parameter 0 is the program name, and it isn't important here...

    switch  (ParameterCount)
    {
    case    1:
        break;

    default:
        ExplainYourself = true;
        break;

    case    3:
        if  (CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, 
            Parameters[1], -1, L"-machine", -1) == CSTR_EQUAL)
            MachineName = Parameters[2];
        else
            ExplainYourself = true;
    }

    if  (ExplainYourself)
    {
        ExplainHowTheCommandLineWorksInMindNumbingDetail();
        return  98052;
    }

The comment about starting as UI or console is an artifact I missed (and will thus shortly disappear).  You can look up 98052 (hint- it's a US postal code, aka ZIP code).

This will upset all those who loathe overloads- it is from a stress test I wrote early on for thrashing KMDF request processing, pnp and power in a random fashion- this snippet does all the up front configuration stuff (there are built-in defaults which you can override from a file specified on the command line- you can optionally display a huge property sheet-based configuration UI and save it all back to a file- settings are also saved to registry and those settings override defaults (but not from file or UI, of course):

/**********************************************************************************************************************************

main

Establishes the stress run settings, possibly with user interaction, then starts the initial mix and loops forever interfering with
the setup until it gets stopped, the machine reboots, the debugger breaks, or you bugcheck.

**********************************************************************************************************************************/

extern "C" int __cdecl main(int, __in char**)
{
    //  Record the global instance handle, so all are copacetic with Win32.
    PropertyPage::InstanceIs((HINSTANCE)GetModuleHandle(NULL));

    //  Seed the PRNG
    FILETIME    Now;
    GetSystemTimeAsFileTime(&Now);
    srand(Now.dwHighDateTime + Now.dwLowDateTime);

    try
    {
        UsingCom                ButThenWhoDoesNot;

        //  We need to get access to the global configuration.  We must pay toll to the gatekeper to do so.

        ControlHolder           WhatMakesUsTick;

        //  Define objects that make up the stress configuration control (and UI)
        ThreadConfiguration&    ThreadControl = WhatMakesUsTick;
        TargetDelays&           TargetDelayControl = WhatMakesUsTick;
        HunterTimeouts&         HunterTimeoutControl = WhatMakesUsTick;
        HunterSectoring&        HunterSectorControl = WhatMakesUsTick;
        TargetLifetimes&        TargetLifetimeControl = WhatMakesUsTick;
        TargetStatus&           TargetReportingControl = WhatMakesUsTick;
        TargetToggler&          TargetToggleCentral = WhatMakesUsTick;
        HunterIdleTimes&        HunterIdling = WhatMakesUsTick;
        HunterPowerUpDelays&    HunterPowerUp = WhatMakesUsTick;
        HunterPowerDownDelays&  HunterPowerDown = WhatMakesUsTick;
        TargetIdleTimes&        TargetIdling = WhatMakesUsTick;
        TargetPowerUpDelays&    TargetPowerUp = WhatMakesUsTick;
        TargetPowerDownDelays&  TargetPowerDown = WhatMakesUsTick;

        //  Now, define the object that represents our operating situation
        MemoryStorage           Configuration;

        //  Size the parameter store, then populate it.
        SizeCalculator  Sizer;

        Sizer << ThreadControl << TargetDelayControl << TargetReportingControl << HunterTimeoutControl <<
            HunterSectorControl << TargetLifetimeControl << TargetToggleCentral << HunterIdling << HunterPowerUp <<
            HunterPowerDown << TargetIdling << TargetPowerUp << TargetPowerDown;

        Configuration << Sizer;

        //  First, set it to the defaults defined by each participating object.
        Configuration << ThreadControl << TargetDelayControl << TargetReportingControl << HunterTimeoutControl <<
            HunterSectorControl << TargetLifetimeControl << TargetToggleCentral << HunterIdling << HunterPowerUp <<
            HunterPowerDown << TargetIdling << TargetPowerUp << TargetPowerDown;

        //  Then, initialize from the registry if there's anything there (settings from last run)

        Registry    LastRun(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\KMDF QA\\IoTargetStress");

        Configuration << LastRun;

        //  Now check the command line- it will tell us if we need to display a UI or initialize from some file.

        int ParameterCount = 0;
        bool    DisplayUI = false;
        PWSTR   FileName = NULL;
        PWSTR*  Parameters = CommandLineToArgvW(GetCommandLineW(), &ParameterCount);

        if  (1 < ParameterCount)
        {
            //  Unfortunately LOCALE_INVARIANT is an XP invention, and this has to run on Win2K
            if  (CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, Parameters[1],
                -1, L"-configure", -1) == CSTR_EQUAL)
            {
                DisplayUI = true;
                if  (2 < ParameterCount )
                    FileName = Parameters[2];
            }
            else
                FileName = Parameters[1];
        }

        //  If we've got a file to initialize from, so be it.

        if  (NULL != FileName)
        {
            FileStorage TreasuredValues(FileName);

            Configuration << TreasuredValues;
        }

        LocalFree(Parameters);

        //  Populate everything with our cool new settings (or perhaps the defaults they gave us in the first place)...
        Configuration >> ThreadControl >> TargetDelayControl >> TargetReportingControl >> HunterTimeoutControl >>
            HunterSectorControl >> TargetLifetimeControl >> TargetToggleCentral >> HunterIdling >> HunterPowerUp >>
            HunterPowerDown >> TargetIdling >> TargetPowerUp >> TargetPowerDown;

        //  If the command line says to display the Configuration UI, then do it.
        if  (DisplayUI)
        {
            Properties          ConfigurationManager;

            //  Populate the property sheet with its pages.
            ConfigurationManager << ThreadControl << TargetDelayControl << TargetReportingControl << HunterTimeoutControl <<
                HunterSectorControl << TargetLifetimeControl << TargetToggleCentral << HunterIdling << HunterPowerUp <<
                HunterPowerDown << TargetIdling << TargetPowerUp << TargetPowerDown;

            if  (ConfigurationManager.Interact() == IDOK)
            {
                //  Pull the updated configuration out of the objects
                Configuration << ThreadControl << TargetDelayControl << TargetReportingControl << HunterTimeoutControl <<
                    HunterSectorControl << TargetLifetimeControl << TargetToggleCentral << HunterIdling << HunterPowerUp <<
                    HunterPowerDown << TargetIdling << TargetPowerUp << TargetPowerDown;

                //  Now, store it off to a file.
                FileStorage Payload;

                Configuration >> Payload;
            }
        }

        //  Save this run's settings off to the registry
        Configuration >> LastRun;

        //  Time to actually start doing the REAL work

Yup, those are cast operators for object references [BTW, they are polymorphic, to boot], overloaded (multiple ways, depending upon the target object) shift operators (iostream-style) and a host of sins that will leave me burning in many folks' idea of programmer Hades for many lifetimes.  Yes, there are "hunters" that fire requests at "targets"- they can even acquire them at will.  I'd show you THAT code, but this is long enough [a static KMDF device context accessor named Hunter::HidingInBushes and some variables named "Nimrod" might give you a general idea, though- and yes, I did write all those KMDF Drivers in C++].

And like all true sinners, I am pretty much unrepentant.  But I do promise never to author a WDK or SDK sample.

Published Friday, February 08, 2008 12:05 PM by BobKjelgaard

Comments

# excuse me while I plod s'more...

*GGGGGGGGRawwrrrrrrrRRRRWrrrrrrrrrr*

Friday, February 08, 2008 9:38 PM by patman

# re: Why You'll Never See a Sample From Me...

Sorry, dude- I was a bit over the top Friday.  I was off my medication (perhaps because I've never been on it).

Some rise,

Some fall,

Some climb

to get to Terrapin.

Bet that only made sense to me :).

Monday, February 11, 2008 4:25 PM by BobKjelgaard
New Comments to this post are disabled
 
Page view tracker