MCA Logo
MCA Online Reference Documentation
Main Index

6.2. Coding guidelines

This section contains some guidelines on how to write well formed MCA2 code.

6.2.1. Class layout

We propose to define functions and variables in the order public, protected and private, each time beginning with functions and ending with variables. We recommend not to declare variables as public. Better define inline function in order to access them.

Example 6-6. A sample class declaration.

class tNewClass::public tBaseClass
{
  public:
    tNewClass();
    ~tNewClass();
    inline int GetMyProtectedVariable() { return a_protected_variable; }

    int an_ugly_public_variable;
  protected:
    int CallMe();
    int CallMeLater();

    int a_protected_variable;
  private:
    int ForInternalUseOnly();

    float i_am_private;
};

6.2.2. Comments

Just like names, all comments written in MCA2 code must be written in english . Please adhere strictly to this rule - it is very difficult for international students to work on code that is commented in other languages.

There are three different ways to signal the start of a comment:

Example 6-7. Sample comments.

//! Short description of tImageLoader
/*! A more detailed description of tImageLoader, which
     spans two lines and says nothing.
*/
class tImageLoader
{
public:

    /*! \param num_streams the number of streams to use
     *  \param delay How many milliseconds to wait before loading the next image
     *
     *  This method constructs a tImageLoader object. This comment is not so useful.
     */
    tImageLoader(unsigned int num_streams, float delay) {

    // create a helper object
    tImage image = new tImage("default.bmp");

    /* Comment out this entire block
      printf("tImageLoader:ctor>> Some useful information\n");
      printf("tImageLoader:ctor>> Some more useful information\n");
      printf("tImageLoader:ctor>> Even more useful information\n");
    */
    }
};

6.2.3. Accessing instance variables

It is a very good idea to choose different names for instance variables (variables declared as members of an object) and other, temporary variables. This helps the reader to find out whether some expression uses a temporary variable (like a function parameter) or some 'global variable' declared in the class definition. It also helps you to avoid problems that can arise when a temporary variable shadows some instance variable with the same name (see the following example).

Example 6-8. Naming conflict between temporary and instance variables.

class tNewClass::public tBaseClass
{
  public:
    tNewClass();
    ~tNewClass();
    void MyNewMethod(int value);
  protected:
    int last_value;
};


tNewClass::MyNewMethod(int value)
{
   int last_value;

   .
   .
   . some code goes here
   .
   .

   // save the value in the instance variable
   last_value = value;

   // ERROR! The temporary variable 'last_value' is used instead of the instance variable. Data will be lost when the method ends.
}

The best way to avoid this naming conflict is somewhat controversial and we therefore offer three different possibilities:

Example 6-9. Accessing instance variables.

class tNewClass::public tBaseClass
{
  public:
    tNewClass();
    ~tNewClass();
    void MyNewMethod(int function_argument);
    void AnotherMethod(int param_variable_three);
  protected:
    float instance_variable_one;
    float _instance_variable_two;
    float variable_three;
};


tNewClass::MyNewMethod(int function_argument)
{
   float temp = sqrt(function_argument);
   this->instance_variable_one = temp;   // using 'this->'
   _instance_variable_two = temp;        // using underscore
}

tNewClass::AnotherMethod(int param_variable_three)
{
   variable_three = param_variable_three; // using param_ prefix
}

In any case, we strongly recommend that you always choose different names for instance and temporary variables. Also, use only one of the methods presented above in your code. Pick the one you like the most and stick to it!

6.2.4. Initializing instance variables in the class constructor

C++ offers a special way to initialize instance variables in the class constructor. This allows for better inheritance and the declaration of const variables without directly specifying their value upon declaration. Therefore, use this special initialization syntax.

Example 6-10. Initializing instance variables.

class tNewClass::public tBaseClass
{
  public:
    tNewClass();
    tNewClass(String& name);
    ~tNewClass();
  protected:
    float _instance_variable_one;
    int   _instance_variable_two;
    String  _my_name;
};

tNewClass::tNewClass():_instance_variable_one(1.4),
                       _instance_variable_two(3),
                       _my_name("default_name")
{
  printf("tNewClass constructed.\n");
}

tNewClass::tNewClass(String& name):_instance_variable_one(1.4),
                                   _instance_variable_two(3),
                                   _my_name(name)
{
  printf("tNewClass constructed.\n");
}

6.2.5. #define

The #define directive may only be used to compile code depending on user defined switches. In order to define constant values we recommend to use constant variables of a specific type. This allows the compiler to do type checking and generate warning or even errors. It also allows to limit the scope of the const variables.

Example 6-11. Correct usage of #define:

#define _DEBUG_

#ifdef _DEBUG_
 printf("debug message\n");
#endif

Example 6-12. Incorrect usage of #define:

Avoid #defining constants:

#define MAX_ITERATIONS 4

if (i == MAX_ITERATIONS)
   printf("finished\n");

Use const variables instead:

const int cMAX_ITERATIONS = 4;

if (i == cMAX_ITERATIONS)
   printf("finished\n");

6.2.6. Sense() and Control()

The Sense() and Control() functions are special functions provided by the tModule class. Inside these functions, you can access the module interfaces using the four methods SensorInput, SensorOutput, ControllerInput and ControllerOutput.

In general, it is a bad idea to call any of the two Controller*-functions in the Sense() method or the Sensor*-functions in the Control() method. This might create circular data paths and cause strange data oscillations whenever multiple Parts are connected. Therefore, avoid mixing these functions; use buffer variables instead.