Tuesday 3 April 2012

Coding Standards: Naming Conventions

Started to the C++ 101 coding standards, by Herb Sutter et al, last night... I love reading C++ books as it gives me a real refresher on the topic and spurs my development work, the last books I read were by Scott Meyers, whom I have a little bit of a coding crush on, his texts are so clear and concise they're lovely to read and if, like me, you are interested in the topic you can re-read them all the time.

I was hoping of more of the same from Herb and co... However, this is the first I've come to read from them and I'm finding it a little annoying.  I've not read much, but I want to just pick one one specific item they mention:

Naming Conventions.

In the first piece (which they correct point out is section zero), they moot naming conventions, they rightly say "pick a naming convention and stick to it" but then they say "and if you've not got one, use this" and go a head and give very brief examples of how they would name things...

Now I hope that as the book unfolds they've not used those naming conventions, as not only did I personally not like them, but I believe firmly on a modern IDE they'd actively come to make coding more work than it needs be, let me explain.

With the code completion tools you get the variables, members, parameters and functions all listed out (usually alphabetically) so if you define three variables

class Foo
{
  public:
int C;
int B;
int A;
};

And then from an instance of the class:

Foo f;

You try to code complete:

f.[Right here you get the code completion options]

And you don't get the order they were declared in.  No, you get them, in every IDE I've used, in alphabetical order, so you see a list

f.[A]
  [B]
  [C]
  
To choose from.  You can then select the item you want, or see it shown to you do you can quickly carry on typing it.  If you remember the variable already then, no harm is done, but you may find a typo or spelling mistake in your declaration if the suggested code completion does not show your required item.  A good double check to use.

Selecting the suggested code completion item is also very trivial, so now think about a more structured class, with functions and members and constants... How can you tell one thing from another, do you want your member spreading all over that code completion list?  Or do you want to be able to see all the members together?  See all the constants and tell the locals from the parameters within functions?

I know I want all those things, so my coding standards differentiate between members, locals, parameters and such by pre-fixing them with:

m_ for members
c_ for constants
g_ for globals
p_ for parameters
l_ for locals

even e_ for enums, though I do admit I use c_ for some enums.

This comes from a little bit of experience that "Hungarian" notation as was is pretty unhelpful at times, and that following variable names with such post-fixes still leaves them in a messy order in the code completion suggestions.

Putting m_ infront of all the members acts as an aggregate to put all the members in the 'm' section, likewise all the locals together and so forth.

Some argue that just underscore be used as a pre-fix for member names, this is often used, if I see code using that style I will adhere to it, but I still maintain the strength added by aggregating with 'm_' over just '_'.

I firmly believe my style allows people to read my code and understand the difference in scope and usage of my variables a lot more readily than other approaches.  I have been openly told this is "Shit", in professional environments, but time and again I've proven its worth to myself, and I hope others, when I have been able to pick up code after 6 months, a year, and recently even as old as five years and gotten straight back into the swing using it.

I have seen other arguments for and against pre-fixing names in any fashion, and even witnessed a strong flow of programmers preferring to post-fix names with just '_' for members.  I personally find this abhorrent, it negates my points about aggregating the members together in the code completion (which even the pre-fixing crowd agree with) and post fixing with '_' leads to terrible code to read:

class Foo
{
   private:
      int Age_;

   public:
   
      Foo (int Age)
      {
         if ( Age > Age_ )
         {
            Age_ = Age;
         }
      }
};

This example shows us, how close "Age_ = Age" could be to a typo of "Age-= Age" and a big headache in the code.

So I suggest you steer away from post-fixing in naming conventions, try pre-fixing and perhaps consider trying my idea out, start to read "l_" as local and "m_" as member in your minds eye as you sound out logic it can be very helpful, and in this example given, it lets you determine all the levels of variable in use:

class Foo
{
   private:
      int m_Age;

   public:
   
      Foo (int p_Age)
      {
         if ( p_Age > m_Age )
         {
            m_Age = p_Age;
         }
      }
};

So, I read that function as:


"if parameter age is greater than member age set member age to parameter age"

Much clearer than the previous, which I can't help but read as

"if age is greater than age score set age score to age"

Shorter to sound out, but a lot more confusing to, especially to less experiences programmers (or beginners).

Now, all this said, I can not deride anyone coding, coding is hard work and takes a lot of concentration, however as we gain experience as programmers we need to stop thinking about just splurging the code out, we need to think about maintainability, about poor souls whom may not have 10+ years in the industry and whom have "seen it all"... So we need to police ourselves, to rein in coding habits and let out coding standards.

This is my naming convention standard, its not better or worse for you than whatever you use now, but for me it works, and I hope I've explained why it works well enough that you perhaps think about using it.

No comments:

Post a Comment