Coding Guidelines: General

This document describes the coding guidelines to be used for MapGuide and FDO. Here are a few reasons why we need coding guidelines:

  • Most of the cost of software goes to maintenance
  • The maintenance is done by many different individuals over the lifetime of a given software product and not the original developer.
  • Guidelines improve readability of the software and help make the learning curve of new developers easier.

These guidelines only apply to new code going forward and do not apply to existing code. That is, existing code does not have to be rewritten to conform. Please use your best judgement when modifying or adding new code to an already existing source file on whether to be consistent to the existing style of the file or to use the new guidelines.

General

This section describes guidelines that are generally applicable to all development languages.

Class Variables

Any variable that needs to be visible outside of a class will expose itself via accessors (either protected or public).  In general we only use private member variables unless there is a very good reason for any other type.  Consequently, variables may be accessed directly only inside the class where they are defined.  All other access is done through the accessor.  This will help us if at some point we need to support multi-threading and distributed objects.

Documentation Standard

Note: Currently the C++ source (for MapGuide) is in Doxygen format.

When you define a new public class, property, or method, you must follow the XML Doc standard. 
XML Doc Standard example:
///<summary>
///Summary of CanIgnoreElement.
///</summary>
///<remarks>
///
///</remarks>
///<param name="element">
///Description of parameter element
///</param>
///<param name="sFileName">
///Description of parameter sFileName
///</param>
/// <returns></returns>
private bool CanIgnoreElement(Element element, string sFileName)

General

It’s important to stay disciplined to avoid getting into the habit of “coding now, documenting later”.  Whenever you add or modify any classes/properties/methods, you must provide/update the documentation as well.  The documentation you write needs to be of sufficient quality so that our technical writers can work with it.  Developers are ultimately responsible for providing the technical information that is required to fully document a class/property/method – not tech writers.  We’ll refer to this documentation as “seed documentation”, and its content should be as close as possible to the end-user reference documentation.  Don't worry too much about formatting – that will be the tech writer's responsibility.
Note that private variables/properties/methods should be documented as well (for code readability).  In this case, however, simply use the normal C++ comment style, e.g.
// the shoe size, including half-sizes
private float shoeSize

Object Creation and Runtime Speed

As with Java, unnecessary object creation is something you should avoid whenever possible.  Just be smart about it when you code.  For example, if a call to a method returns a new object and you call that method multiple times in the same context, then change your code to call this method only once and reuse the returned value.  Another example: use private helper objects when it makes sense to avoid unnecessary object creation.

Threading Guidelines

It’s important to follow strict threading rules when writing new code. Some of the code will be used in a multithreaded deployment environment. Failing to follow multi-threading guidelines will be costly in the future. There are detailed guidelines provided by the Design Guidelines for Class Library Developers. That should be reviewed. Here are some points from those guidelines:

  • Avoid providing static methods that alter static state
  • Be aware of method calls in locked sections
  • Avoid the need for synchronization if possible
  • Avoid using global variables

Source File Naming

The naming of source code files will match the class names.  For example, if I have a class called ClassA then all source files for that class will also have the name ClassA.  In C++, then there are two files: the source file with extension .cpp and the header file with extension .h.  No mismatches between class names and file names will be allowed!
One consequence of this rule is that you can only define one class/enumeration/struct/interface per file pair.

Deleting Files and Projects

Eventually everyone needs to remove files and/or projects from the main source.  If you’re removing files, you update the Visual Studio solution file and if applicable the Linux build script.  Having done this, you still need to remove the file / project from the source control application.

General Structure of Files

We would like all source files to have a similar structure:

  • Copyright
    //
    // Copyright (C) 20??-20?? Copyright Holder
    //
    // This library is free software; you can redistribute it and/or
    // modify it under the terms of version 2.1 of the GNU Lesser
    // General Public License as published by the Free Software Foundation.
    //
    // This library is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    // Lesser General Public License for more details.
    //
    // You should have received a copy of the GNU Lesser General Public
    // License along with this library; if not, write to the Free Software
    // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
    //

  • Class declaration; includes seed documentation
  • Constant declarations
  • Local variable declarations
  • Constructors
  • Properties/methods

Tabs

  • Use 4 spaces for tabs. Do not use tabs in the file.

Stylistic Guidelines

General

Use the next-line convention for curly braces:
public void Foo()
{
}

Compiler Warning

This guideline strongly encourages all compiler warnings to be treated as errors and therefore they should be fixed.

Break and goto

Goto and break make the code very hard to read.
The guideline is to avoid using break statements inside loops and to forbid the use of goto.

 if, if-else, if else-if else Statements

The if-else statements should have the following form:

if (condition)
{
    statements;
}
if (condition)
{
    statements;
}
else
{
    statements;
}
if (condition)
{
    statements;
}
else if (condition)
{
    statements;
}
else
{
    statements;
}

for Statements

A for statement should have the following form:

for (initialization; condition; update)
{
    statements;
}

An empty for statement

        for (initialization; condition; update);

while Statements

A while statement should have the following form:

while (condition)
{
    statements;
}

An empty while statement should have the following form:

        while (condition);

do-while Statements

A do-while statement should have the following form:

do
{
    statements;
} while (condition);

 switch Statements

A switch statement should have the following form.

switch (condition)
{
    case XYZ:
        statements;
        break;
    case ZYX:
        statements;
        break;
    default:
        statements;
        break;
}

Every switch statement should include a default case.

try-catch Statements

A try-catch statement should have the following format:

try
{
    statements;
}
catch (ExceptionClass1 * e)
{
    statements;
}
catch (ExceptionClass2 * e)
{
    statements;
}

Return Value

The use of a single return within a method or function is encouraged.

The following structure:

if (booleanExpression)
{
    return true;
}
else
{
    return false;
}

should be:
return booleanExpression;

Also,

if (condition)
{
    return x;
}
return y;

should be:

        return (condition ? x : y);

Expressions with ==

The following order should be used because it will help catch the accidental use of “=” when “==” was intended at compile time:

if (constant == variable)
{
    // do something
}
else
{
    // do something else
}

Pointers

Normal Pointers

A pointer should always be checked before using. You should never assume that the pointer will be valid.
This is a good coding style and should be done.

if (pData != NULL)
{
  int nSize = pData->GetSize();
  ...
}
This is a bad coding style and should not be done.
int nSize = pData->GetSize();
 

When you are no longer using a pointer you should set it to NULL so that it is clear that it is no longer valid.

...
delete pData;
pData = NULL;

Parentheses

Even if the operator precedence seems clear to you, it is best to use parentheses to avoid any confusion by others.

if (a == b && c == d)     // Bad
if ((a == b) && (c == d)) // Good

Globalization

All strings which need to be localized must be placed into resource files.

Strings which do not need to be localized can remain in the code.  However, you should indicate the string is non-localizable by adding a NOXLATE comment, e.g.
    if (filename.EndsWith(".txt"))    // NOXLATE
    {
        ...
    }
The comment tells the globalization team that the author of the code explicitly wants to exclude the string from being localized.
Exception string messages should be globalized if possible.

Special Comments

Use BOGUS in a comment to flag something that is bogus but works. Use FIXME to flag something that is bogus and broken. Use TODO when something needs to be completed but working. Code that is not implemented or not tested should raise an exception. For every instance add necessary comment to further explain the situation.

C++ Specific Guidelines

This section details some of the C++ specific guidelines.

Member variables

Prefix names of member variables using “m_”.

Strings

Internally

The internal string format to use will be wstring.

Across Wire

The UTF-8 standard will be used for transmitting strings across the wire (e.g., between MapGuide client, web tier, and server).

Repository

The resource repository used by MapGuide will use the UTF-8 standard for storing strings.

API Boundary

MapGuide will use the following string format for API boundaries in the web tier and the server:
const wstring&

Smart Pointers

Our Code

The MapGuide Ptr template class acts as a smart pointer and should be used whenever possible.
Example:
MgRepository* MgServerResourceService::CreateRepository(CREFSTRING repositoryType, CREFSTRING repositoryName)
{
    Ptr<MgRepository> repository = 0;

    MG_RESOURCE_SERVICE_TRY()

    ResourceServiceUtil::CheckArgument(!repositoryName.empty());

    if (RepositoryType::Solution == repositoryType)
    {
        repository = new MgSolutionRepository(repositoryName, this);
    }
    else if (RepositoryType::Session == repositoryType)
    {
        repository = new MgSessionRepository(repositoryName, this);
    }
    else
    {
        throw new MgInvalidRepositoryTypeException(__FILE__, __LINE__);
    }

    MgResourceServiceUtil::CheckMemory(repository);

    MG_RESOURCE_SERVICE_CATCH_AND_THROW()

    assert(0 == mgException);

    (*repository).AddRef();     // Ownership is transferred to the caller

    return repository;
}

3rd Party Library

Wherever possible the standard C++ smart pointer should be used when dealing with 3rd party libraries.
The following is some sample code that uses the standard C++ smart pointer along with a typical MapGuide Exception handling mechanism:

Bar* Foo:CreateBar()
{
    auto_ptr<Bar> bar;

    MAPGUIDE_TRY()

    bar = auto_ptr<Bar>(new Bar);
    assert(0 != bar.get());
    bar.DoSomething(); // might throw an exception

    MAPGUIDE_CATCH_AND_THROW()

    return bar.release(); // release ownership of the smart pointer
}

Standard C++ smart pointer notes:
1.      Never use auto_ptr objects as elements of STL containers because auto_ptr does not quite meet the requirements of a type you can put into containers (i.e. copies of auto_ptrs are not equivalent).
2.      Never use an array as an auto_ptr argument because auto_ptr's destructor invokes only non-array delete.
The following code is a cleaned up version of the above sample code using some helper macros.

Bar* Foo:CreateBar()
{
    char* bar = 0;

    MAPGUIDE_TRY()

    bar = new char[256];
    assert(0 != bar);
    DoSomething(bar);

    MAPGUIDE_CATCH()

    if (0 != mgException)
    {
        delete[] bar;
        throw mgException;
    }

    return bar;
}

Note that MAPGUIDE_TRY(), MAPGUIDE_CATCH(), and MAPGUIDE_CATCH_AND_THROW() macros can be anything specific to a MapGuide component.

Example macro definitions for the resource service:

#define MG_RESOURCE_SERVICE_TRY()                                             \
    MapGuideException* mgException = 0;                                       \
                                                                              \
    try                                                                       \
    {                                                                         \

#define MG_RESOURCE_SERVICE_CATCH()                                           \
    }                                                                         \
    catch (XmlException* e)                                                   \
    {                                                                         \
        mgException = new MgDbXmlException(*e);                               \
    }                                                                         \
    catch (DbException* e)                                                    \
    {                                                                         \
        mgException = new MgDbException(*e);                                  \
    }                                                                         \
    catch (MgException* e)                                                    \
    {                                                                         \
        mgException = e;                                                      \
    }                                                                         \
    catch (exception* e)                                                      \
    {                                                                         \
        mgException = new MgStandardException(*e);                            \
    }                                                                         \
    catch (...)                                                               \
    {                                                                         \
        mgException = new MgUnClassifiedException(__LINE__, __WFILE__);       \
    }

#define MG_RESOURCE_SERVICE_CATCH_AND_THROW()                                 \
    MG_RESOURCE_SERVICE_CATCH()                                               \
                                                                              \
    if (mgException != 0)                                                     \
    {                                                                         \
        throw mgException;                                                    \
    }                                                                         \

Enumerations

Please use the following format when declaring enumerations.

Example 1:
enum MgDimensionality
{
    mdXY = 0,     /// XY
    mdXYM,        /// XY + Measure
    mdXYZ,        /// XYZ
    mdXYZM,       /// XYZ + Measure
};

Example 2:
enum MgConnectionState
{
    mcsOpen = 0,   /// Connection is still open
    mcsClose,      /// Connection has been closed
    mcsBusy        /// Connection is busy processing other request
};

You will notice that the 1st letter of each word is part of the enumeration value.
Also, when using enumerations in your code ALWAYS do the following:
MgConnectionState::mcsOpen
Do NOT do the following:
mcsOpen

.NET Specific Guidelines

For those modules that are written in .NET, the following guidelines should also be used in addition to the guidelines that apply in the previous sections.

Consistency

We should understand and follow the standards/design guidelines recommended by Microsoft for .NET.  In Visual Studio .NET, you can find these under Design Guidelines for Class Library Developers.  Alternatively, you can go to the help index and navigate to:
Visual Studio .NET > .NET Framework > Reference > Design Guidelines for Class Library Developers
The guidelines discuss topics such as naming conventions, how to decide whether something should be a property or method, proper use of enumerations, delegates, and events, and lots of other things.  You should become generally familiar with these guidelines.

Documentation format

When you define a new public class, property, or method, you must follow the XML Doc standard.  Typing three slashes “///” just before a class/property/method declaration will bring up a skeleton XML Doc header in .NET, for C++ you will have to enter this.  You then simply need to add information in the different fields.  Note that if you fail to add the XML Doc header for .NET, the code may not compile (this is an option that we will enable for all our projects).
XML Doc Standard example:
///<summary>
///Summary of CanIgnoreElement.
///</summary>
///<remarks>
///
///</remarks>
///<param name="element">
///Description of parameter element
///</param>
///<param name="sFileName">
///Description of parameter sFileName
///</param>
/// <returns></returns>

private bool CanIgnoreElement(Element element, string sFileName)

Structure of a source file

We would like all Tux files to have a similar structure:

  • Copyright info
  • every source file must have this at the top
  • Import of .NET namespaces (If applicable)

- ideally sorted alphabetically

  • Import of local namespaces (If applicable)

- ideally sorted alphabetically

  • Namespace info (If applicable)

- as mentioned above, the namespace matches the folder path of the source file(s)

  • Class declaration

- includes seed documentation

  • Constant declarations
  • Local variable declarations
  • Inner classes separated by section comments (If applicable)
  • Constructors
  • Properties/methods

For example:
//
// Copyright (C) 2004-2006 Autodesk, Inc.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of version 2.1 of the GNU Lesser
// General Public License as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
using System;
using System.Runtime.Serialization;

using Autodesk.Gis.Graphics;
using Autodesk.Gis.Utilities;

namespace Autodesk.DataAccess.Adp.Tools
{
    /// <summary>
    /// Provides the base functionality for ...
    /// </summary>
    [Serializable]
    public class MyClass : ISerializable
    {
        //-------------------------------------------------------
        // Constants
        //-------------------------------------------------------
        Constants

        //-------------------------------------------------------
        // Variables
        //-------------------------------------------------------
        Variables

        //-------------------------------------------------------
        // Inner classes
        //-------------------------------------------------------
        Inner classes

        //-------------------------------------------------------
        // Constructors
        //-------------------------------------------------------
        Constructors

        //-------------------------------------------------------
        // Properties / Methods
        //-------------------------------------------------------
        Properties / Methods
    }
}

Member variable usage

A standard for C# is to preface all references to instance variables/properties/methods with the “this” keyword.  For example:
public void Foo()
{
    // update an instance member variable
    this.index = 0;

    // call an instance member function
    this.Foo();
}

All static variables are named using Pascal casing according to the .NET standard.  When referencing static constants or methods, always prefix it using the actual name of the class defining the constant or static method.  For example:
public class Foo1
{
    /// <summary>
    /// The max iterations to use.
    /// </summary>
    public int maxIter;

    /// <summary>
    /// Default value for max iterations.
    /// </summary>
    static public int defaultMaxIterations = 10;

    ...
}

public class Foo2 extends Foo1
{
    ...

    this.maxIter = this.defaultMaxIterations;  // wrong – not an instance variable
    this.maxIter = Foo2.defaultMaxIterations;  // wrong - subclass doesn't declare it
    this.maxIter = Foo1.defaultMaxIterations;  // correct

    ...
}