C H A P T E R  3

image

Style Guidelines for packetC Programs

Introduction to packetC Style Guidelines

This chapter covers packetC coding-style recommendations based on common C++ development community practices. The following recommendations establish the guidelines that CloudShield-developed software is expected to follow within packetC. As always with style guidelines, these are suggestions and individual third-party developers may choose to follow their own style guidelines and packetC compilers shall not be your jury.

While packetC has many traits similar to C and C++, there are also several deviations. As such, C or C++ style guidelines developed elsewhere do not cover all aspects of the packetC grammar to which style applies. This guide is intended to cover areas common to C++ and packetC as well as those areas specific to packetC.

The CloudShield packetC Integrated Development Environment (IDE) provides an editor that improves the readability of code by color-coding, and some automated features for formatting code automatically to conform to the packetC style. These are tools and are considered outside of the guidelines of the style guide.

Meaning of Wording in packetC Style Guidelines

As packetC is a language for networking bearing its roots in representing network protocols, identifying the source for definitions of words significant to developing guidelines in style must be Internet RFCs. The following portions of text are modeled on the best practices defined in RFC 2119.

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described below and have special meaning in this chapter on packetC Style.

  • MUST—This word, or the terms “REQUIRED” or “SHALL”, means that the definition is an absolute requirement of the style guide.
  • MUST NOT—This phrase, or the phrase “SHALL NOT”, means that the definition is an absolute prohibition of the style guide.
  • SHOULD—This word, or the adjective “RECOMMENDED”, means that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.
  • SHOULD NOT—This phrase, or the phrase “NOT RECOMMENDED”, means that there may exist valid reasons in particular circumstances when the particular behavior is acceptable or even useful, but the full implications should be understood and the case carefully weighed before implementing any behavior described with this label.
  • MAY—This word, or the adjective “OPTIONAL”, means that an item is truly optional. One may choose to follow the guideline or not and it will have no effect on whether the code is following the style guidelines.

Last Clarification

These guidelines on style for packetC are intended to improve the readability of code as well as help promote a consistent style, making it easier to share code within the packetC community. This guide is not expected to be exhaustive of every scenario and it is expected that the rationale to violate or differ in style from this guide will be hotly debated. Please remember these guidelines are not requirements to a particular coding style forced on the entire packetC development community.

Naming Conventions for Variables, Types, and Functions

Throughout this document, four different cases are used for names to help identify the type of a name when seen in code. As packetC, like C, is a case-sensitive language, these styles not only aid in identification of types, but also in assurance of scope benefiting auditing of code. Listed below are the four difference cases leveraged in the style guide:

  • UPPERCASE—All characters are uppercased.
  • lowercase—All characters are lowercased.
  • lowerCamelCase—The first character is lowercased with the first character of each word following capitalized.
  • UpperCamelCase—The first character is uppercased with the first character of each word following capitalized.

Scoping in packetC is critical to dealing with parallel processing. There are two major scopes that affect naming conventions—namely, global and packet scope. Global scope data are visible to all packets being processed within packetC, while packet scope data are only visible within the processing of the current packet. The third scope, block scope, is a tighter form within packet scope and for naming follows packet scope guidelines. It is important to easily distinguish data that are global and have potential impacts from parallel processing from those data elements that are safe from the impacts of parallel processing. The packetC style guidelines present a method for distinguishing variables from types and functions while also distinguishing variables in different scopes. As always, variables should be declared in as small a scope as possible to protect against conflicts.

Variables

Variable names are case sensitive and can only begin with a letter. The rest of the characters can be letters, digits, and underscore (“_”) characters. No white space is permitted in variable names for obvious reasons. When choosing a variable name, use whole words instead of abbreviations, doing so makes the code easier to read and self-documents the variable. The use of different cases and the dollar sign character (“$”) are found in some system constructs.

Variable names for packet and block scope must use lowerCamelCase. The following are some simple examples of variable declarations using lowerCamelCase:

int     counter;
byte    myChunkOfPacket[50];
short   oneVlanTag;

When naming variables, it is generally best for the length of the name to go hand-in-hand with the scope of usage of the variable throughout the program. Local loop counters defined in the code close to its entire usage are often short names like i, j, k, or l while names used more broadly, such as myChunkOfPacket should be more verbose to make code more easily understood without the requirement for over-commenting.

Global scope variables must use lowerCamelCase with a trailing underscore (“_”) to identify that the variable is of global scope. The following are a few examples of global scope variable declarations:

int  capturePacketsActivated_;
int  contextPacketCounter_[96];
int  globalVariable_;

Using underscores as a prefix is invalid in packetC as variables must start with a letter and underscores are otherwise only suggested within UPPERCASED constants. The use of the trailing underscore was chosen to follow C++ class scope naming conventions.

Named constants, including enumeration values, must be all UPPERCASE using an underscore (“_”) to separate words.

enum byte MySize { MYSIZE_SINGLE = 32, MYSIZE_DOUBLE = 64, MYSIZE_QUAD };
enum byte StorageType { STORAGETYPE_BYTE, STORAGETYPE_SHORT, STORAGETYPE_INT };
const int FULL_MASK = 255.255.255.255;
const int MPLS_TAG_BYTES = 4;

The capitalization of named constants follows C++ conventions. In packetC, the use of named constants not only continues the security benefit of values that cannot change, but it also has important performance benefits generating a greater amount of use in packetC than normally found in C++. The visual distinction of named constants in packetC aids in visually distinguishing illegal cases of using named constants as the target of assignments.

While not a variable, #define macros must use UPPERCASE form following the form of constants:

#define NUMBER_OF_PORTS 4
const int NUMBER_OF_BLADES = 10;

In packetC, you may find variable types declared starting with a dollar sign character (“$”). The dollar sign is a special character only legal as part of a system preloaded type or construct that cannot be allocated by the user. To differentiate these variables and constructs, they are presented using the UPPERCASE naming convention. Three common types, namely $PACKET, $PIB, and $SYS, appear in packetC that use this naming convention. While predefined system variables and types are rare, should they appear in a packetC system, they must follow the UPPERCASE form. While special treatment may have been applied to create these types and variables, they may be referenced like any other variable.

Types

Type names must use the UpperCamelCase naming convention to signify that they are a type, as opposed to a variable instantiating the type. A few examples of type declarations along with instantiations are shown to help illustrate how this allows for distinction of variables from types even when named similarly:

struct SimpleStruct { int x; int y; };   // type declaration
SimpleStruct simpleStruct;               // shown in use
typedef int IpAddress;                   // type declaration
IpAddress srcIp, dstIp;                  // shown in use

// SimpleHeader struct type
descriptor SimpleHeader { int field1; } simpleHeader at pib.l2Offset;

The differentiation of variables and types allows for easy identification and distinction within code. When defining generic types and variables, the type name should match the variable name as shown with type SimpleStruct and variable simpleStruct above.

Enumerations follow the form of any other type and must use UpperCamelCase. In addition, some further implications apply to the named constants within an enumeration. Named constants shall use UPPERCASE as previously defined, although in the case of enumerations they shall also be preceded with the name of the enumeration as shown below:

enum byte MySize { MYSIZE_SINGLE = 32, MYSIZE_DOUBLE = 64, MYSIZE_QUAD };
enum byte StorageType { STORAGETYPE_BYTE, STORAGETYPE_SHORT, STORAGETYPE_INT };

Similar to system preloaded variables, system preloaded types start with a dollar sign character (“$”). The dollar sign is a special character only legal as part of system preloaded variables or constructs that cannot be allocated by the user. To clearly differentiate these constructs they are presented using the $UPPERCASE naming convention. $PACKET, $PIB, and $SYS are three system preloaded types that are consistently seen in the parameter list of function main(). Throughout the system header file, “cloudshield.ph”, the use of system types appears quite often. While special treatment may have been applied to create these or perform actions upon them, these are referenced like any other variable or construct. The following shows the type definition of the $PACKET type found in “cloudshield.ph”:

typedef byte $PACKET[9 * 1024 - 1];

In the case of $PACKET above, the presentation to the user is a standard type. In packetC, however, the packet is a variable of a special system type treated very differently with methods that only operate on the packet.

Functions

Names representing methods or functions follow the lowerCamelCase form similar to variables with a few differences that help distinguish them from variables. First, functions must start with a verb to show action in the name. Second, functions have a specific form—namely, the use of parentheses and parameters following the name to distinguish them from variables.

byte invertByte (byte x)
{
    return ~x;
};

The following shows a simple usage of the above example to help articulate the distinction between variables and functions:

int invertedByte, myByte;
invertedByte = invertByte(myByte);

Additional Conventions for Naming Variables, Types, and Functions

The use of abbreviations and acronyms must not be UPPERCASE when used in CamelCase names. Uppercasing words within names causes issues when the word sits at the beginning of a name as well as for words that follow the uppercased portion of the name. A few common examples along with the common pitfall are shown below:

int sourceIp;                                 // Do Not Use sourceIP;
int getHttpCommand();                         // Do Not Use getHTTPCommand;
struct UdpHeader { short destPort; ... };     // Do Not Use UDPHeader

When choosing names, they should be clear and not cryptic as well as leveraging English. Abbreviations should be avoided. In addition, avoid using cute variable names or single character names with the exception of block scope loop counters. Naming variables and functions in a meaningful way helps convey their intended usage and avoids the need for complex comments being required to discern what is being done in the code.

int packetNumber;    // Avoid abbreviations like pktNbr and foreign language like packetNombre
int averageBitRate;  // Not avgBitRt
int whileRomeBurns;  // Bad! No relation to code or problem domain.

Some good examples are shown below:

int     xmlPreableHeader;
short   treeElement;
byte    tagAttribute;
void    writeXmlTree();
long    timeOutMs;
int     pauseHours;

When considering abbreviations, be appreciative of commonly abbreviated words versus those that are not. For example, in networking, IP is a common abbreviation where InternetProtocol is not expected. Furthermore, CloudShield is commonly spelled out while CS is not expected. As such, http, tcp, and other common abbreviations are not suggested to be spelled out to comply with abbreviation guidelines.

Avoid the use of digits in the names. The exception is when it is meaningful to the name:

int    ipv4Offset;      // Good!
int    str2;            // Bad!

While abbreviations are recommended against as a style guideline, a competing dilemma is the suggestion to avoid overly long names. If variable and function names are becoming considerably long, reconsider the name as shown in the example:

byte        ipv6OffsetToPacketPayLoad;     // Bad!
byte        ipv6Offset;                    // Good!

Variables must be used for only one purpose and not have different meanings in different parts of the code. In some case, generic variables are used multiple times for loop counters, which is fine since their meaning never changes from being a loop counter. For generic variables that are not a base type, these should have the same naming as their type. Combining the name of the variable with the type name allows the user to quickly figure out the variable type.

typedef short PortNumber;                              // Simple Type Declaration
PortNumber portNumber;                                 // Generic Type Used Only Once
PortNumber sourcePortNumber, sestinationPortNumber;    // Generic Type Used For Multiple
Variables

Enumerations are types that must follow type naming conventions. In addition, enumeration names should be singular as in enum StorageType {...} and not enum StorageTypes as the usage represents a singular item even though the enumeration definition appears plural. Given that the name of the enumeration is carried over into the named constants within the enumeration, the use of a singular enumeration name will make much more sense. Consider the following using a plural definition:

// Bad use of plural definition
enum byte Colors { COLORS_GOLD, COLORS_SILVER, COLORS_BRONZE};
Colors customerColor;
customerColor = COLORS_GOLD;

or the following using a singular definition which reads a bit easier in English:

// Good use of singular definition
enum byte Color { COLOR_GOLD, COLOR_SILVER, COLOR_BRONZE};
Color customerColor;
customerColor = COLOR_GOLD;

All variables that are initialized, either as a constant, or at run time shall be initialized where declared. A function declaring several variables at the start of a function shall not initialize those variables scattered throughout the source code of the function. For variables that cannot be initialized where they are declared, do not initialize with a dummy value just to conform with a style guideline as this will only counter the benefits being aspired to of clarity within the code.

int maxMplsDepth;
maxMplsDepth = 4;

For functions that are returning a predefined value of true or false, the function name should start with is such that it drives a consistency in expectation of return code.

systemInitialized = isInitialized();

The goal is to remove the tendency toward a variety of bit, flag, or other representations of Boolean responses and to differentiate from function returning the base type as there is no true Boolean in packetC.

Source Code Presentation, Indentation, and Form

The form of indentations, alignment, and whitespace usage is a very personal thing and spurs many religious arguments. For packetC, following the BSD/Allman style of indentation became the basis of the suggestions found throughout the next section. Some call this style ANSI as it was used heavily within the ANSI C specifications for examples. This style provides a lot of whitespace that aids in the readability of code and drives clarity to nesting of conditionals that are at the heart of packet processing. The biggest area in which this will appear is the form of indentation following functions and conditionals where the braces occupy a line of their own as shown below:

control
{
  statement;
}

As a result, packetC has a very whitespace-intensive style. The sections below highlight in more detail the implications of this form.

General Source Code Form

Module source code and any included header files should keep their content within 80 columns to ensure that printing source code and automated extraction of comments do not run into issues in formatting. The 80-column rule should also apply to data files if it is anticipated that they will be edited by humans. For data files not for human consumption, rows should not span more than one row in a variable's data set.

Special characters like TAB and page break must be avoided and editors should leverage replacement of tabs with spaces. Within editors, set tabs to convert to a predefined number of spaces. The packetC tab conversion to space recommendation is 2 spaces. An indentation of 1 is too small to emphasize the logical layout of the code, while an indentation larger than 4 makes deeply-nested code difficult to read and increases the chance that the lines must be split. Choosing between indentation of 2, 3, and 4, 2 and 4 are the more common, and 2 chosen to reduce the chance of splitting code lines given the constraint of 80 character lines and judicious use of whitespace in the packetC style.

In packetC, the general layout of source code follows much of what is found in C++ with some throwbacks to C style where important for the deviations of packetC. With regard to whitespace within code, the general packetC rule is more is better with the focus of ease of reading to support audit and review of code benefiting accuracy and security. A few good tips are:

  • Surround operators with whitespace.
  • Follow punctuation such as commas, colons, and semicolons with whitespace.
  • Reserved words should be followed with whitespace.

A few examples of what to do and not to do are shown below:

z=(x+y)>>MAX_BITS;                      // Too tightly packed to read.
z = (x + y) >> MAX_BITS;                // Easier to read.
while(x==true){y++;};                   // Too tightly packed to read.
while (x == true) { y++; };             // Easier to read.
case HTTP_PORT:                         // No space before colon.
case HTTP_PORT :                        // Placed a space before colon.
for (i = 0; i < 5; i++ ) { . . .        // Well spaced for readability.

The goal is to make statements be easily discerned with a quick scan of the code and not blend in too much. While whitespace within a line is important, it is also important to place space between lines where appropriate to help segment the code. Placing a space after a section of code initializing variables preceding the rest of a function can help provide a logical break that might not otherwise be discerned such as shown below:

Command getHttpCommand(int payloadOffset)
{
  int endOffset;
  endOffset = payloadOffset + 4;

  if (pkt[payloadOffset:endOffset] == "GET ")
  {

The goal is to help segment the code visually to make it easy to read. In addition, use alignment to help with the ability to read the code quickly as shown below:

if (pkt[payloadOffset:endOffset] == "GET ")
{
   returnCommand = COMMAND_GET;
}
else if (pkt[payloadOffset:endOffset] == "POST")
{
   returnCommand = COMMAND_POST;
}
else
{
   returnCommand = COMMAND_OTHER;
};
return returnCommand;

or something like the following:

switch ((int) pkt[payloadOffset:endOffset)
{
   case (int) STRING_GET :
      returnCommand = COMMAND_GET;
      break;
   case (int) STRING_POST :
      returnCommand = COMMAND_POST;
      break;
   default :
      returnCommand = COMMAND_OTHER;
      break;
}
return returnCommand;

In some cases, you may find that whitespace competes with other style guidelines. It is best to focus on readability with ample whitespace over other layout guidelines in those cases, although these areas can get very subjective.

If a statement spans more than one line due to the length of the contents of the statement, formatting must make the fact that the statement is split very obvious. In addition, the split must occur at a logical boundary, such as following a punctuation or operator. The following line shall be indented to highlight the continuation and distinguish it from other statements at the same indention level. The following example shows a function declaration that is too long to fit on a single line along with an indention of the continuation that uses whitespace to highlight the relationship with the declaration and contrast it from the start of the function:

PacketEnumerations getPacketEnumeration (int enumerationOffset, int enumerationLength,
                                         int enumerationConversion)
{
   int i, j, k;
   i = j = k = 0;

Include Files and Include Statements

All header files must contain an include file guard macro. A guard macro protects a file against being included more than once that can cause unforeseen side effects in large programs such as compilation errors and naming conflicts. A guard macro uses two pre-processor directives at the start of the include file and one at the end of the include file. Naming convention states that defines follow UPPERCASE form and guards should also leverage a meaningful relationship to the filename and its relationship to a broader library should that be relevant.

#ifndef CLOUDSHIELD_FLOWLIBRARY_PH    // First line of include
#define CLOUDSHIELD_FLOWLIBRARY_PH
       ...                            // Body of include goes here
#endif                                // Last line of include: CLOUDSHIELD_FLOWLIBRARY_PH

Include statements should be sorted and grouped. Sorted by their hierarchical position in the system with low-level files included first, followed by higher-level include files. Leave an empty line between groups of include statements.

#include <cloudshield.ph>
#include <protocols.ph>

#include <cloudshield-flowlibrary.ph>

#include "includesmycompanymore-custom-protocols.ph"

Include file paths must never be absolute and compiler directives should instead be used to indicate root directories for includes. System includes should leverage system include directives (“<file>”) while application-specific includes should use local includes (“file”).

Furthermore, include statements must be located at the top of a file only, and deeply hidden include files with the body of a source file should be avoided. In packetC, the one case where include files are nested within the body of source is for dataset initialization. These should be restricted to .px data files in the form shown in the example below:

const int URL_HASHES_[500] = {
#include "url-hashes.px"
};

int x_[50][50] = {
#include "arrayxdata.px"
};

Functions and Declarations

There are several guidelines that are critical to proper behavior of functions in packetC. At the highest level, indentation follows similar rules to other control structures with the brace on the line following the function declaration at the same indentation level as the declaration. In addition, when using prototypes for functions in header files the prototype and function declaration should match identically, not just in parameter order and types. In addition, a function shall only have one return statement to ensure readability.

The return value parameter should be first on the line followed by the function name and all formal parameter declarations in parentheses. The opening brace of the function body should be alone on a line beginning in column 1. Local declarations and code within the function body should be tabbed over one stop. If the value returned requires a long explanation, it should be given in the block header and not inline with the code.

The following example highlights a few of these principles where Command is an enumeration type:

Command getHttpCommand(int payloadOffset)
{
  if (pkt[payloadOffset:payloadOffset + 4] ==  STRING_GET)
  {
     commandReturn = COMMAND_GET;
  } else
  {
     commandReturn = COMMAND_OTHER;
  };
  return commandReturn;
}

As previously noted, functions use lowerCamelCase naming and begin with a verb and should be small and easy to understand upon review. Large and complex functions should be broken out into multiple functions to simplify readability and leverage inline directives at the call site when possible to reduce function call overhead. All functions should be preceded by the requisite comment block shown later in this chapter.

For functions, do not default to always using int as the return type. If the function does not return a value then it should be given return type void to clearly articulate that. In addition, the use of try and catch with controlling the evaluation within the function should be used instead of returning complex types that require conditional evaluation of success beyond true/false. In other words, return types that are not base types should contain the resulting data from the function and not a complex response code as those complexities should be addressed within the function.

If a group of functions all have similar parameters and use, it helps to call the repeated variable by the same name in all functions. Conversely, avoid using the same name for different purposes in related functions. Like parameters should also appear in the same place in the various argument lists.

General Conditionals Formatting

Conditional form is focused on readability with plenty of whitespace to ensure ease of debugging and evaluation. This form follows from the simplest evaluation through to complex conditionals. Even for the most simplistic conditionals, split the conditional over more than one line and include braces as follows:

if (isFinished == true)
{
  finishedEvaluations++;
}

Similar to other sections in this chapter emphasizing using whitespace for clarity and splitting lines at clear break-points, a long string of conditional operators should be split onto separate lines. The following expression:

if ( foo.next == NULL && totalCount < needed && needed <= MAX_ALLOT
       && serverActive(currentInput))
{
   ++totalCount;

should be written as follows in packetC:

if         (  foo->next == NULL      &&
              totalCount < needed    &&
        needed <= MAX_ALLOT  &&
        serverActive(currentInput) )
{
++totalCount;

Similarly, elaborate for loops should be split onto different lines.

for ( (  (currentLoopCounter = 0) & (augmentedLoopVariable=0)  );
   currentLoopCounter <= MAX_LOOP_VALUE;
   currentLoopCounter = currentLoopCounter + augmentedLoopVariable )
{

For loops with a null body, the null statement must be alone on a line and commented so that it is clear that the null body is intentional and not missing code. Also, even for null body conditionals, braces must be used.

while (destination++ = source++)
{
        ;         /* VOID - Null Body */
};

A compound statement is a list of statements enclosed by braces. The simple form of a compound statement is shown below:

control
{
  statement;
  statement;
}

When a block of code has several labels (unless there are a lot of them), the labels are placed on separate lines. The fall-through feature of the C switch statement (that is, when there is no break between a code segment and the next case statement) must be commented for future maintenance. A blank line should be inserted between the case test groups for readability.

switch (expr)
{
case ABC :
case DEF :
  statement;
  break;

case UVW :
  statement;
  /*FALLTHROUGH*/

case XYZ :
  statement;
  break;
}

Here, the last break is unnecessary, but it is required to prevent a fall-through error if another case is added after the last one. The default case, if used, should be last and does not require a break if it is last.

Specific Conditionals Forms

While the aforementioned style guidelines can be applied to most conditional forms and result in the specific guidelines, a specific description of each conditional form is listed below.

Whenever an if-else statement has a compound statement for either the if or else section, the statements of both the if and else sections should both be enclosed in braces (called fully bracketed syntax). The else part of an if-else statement should appear on the same line as the close brace.

if ( expression )
{
  statement;
  statement;
} else if ( expression )
{
  statement;
} else ( expression )
{
  statement;
};

Do-while loops should always have braces around the body and the while statement sits on the same line as the closing brace as shown below:

do
{
  statement;
} while ( conditionExists );

A while statement should have the following form:

while (condition)
{  
  statement;
}

A for statement should have the following form:

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

A switch statement should use the following form where the conclusion of statements from one case are separated from the start of the following case statement by vertical whitespace and no statements are on the same line as the case. Breaks must be used in all cases with only default, which must be the last case, being able to contain statements without a break.

switch (expr)
{
  case ABC :
  case DEF :
    statement;
    break;

  case UVW :
    statement;
    /*FALLTHROUGH*/

  case XYZ :
    statement;
    break;

  default :
    statement;
}

Note that each case keyword is indented relative to the switch statement as a whole. This makes the entire switch statement stand out. Note also the extra space before the : character. The explicit fall-through comment should be included whenever there is a case statement without a break statement.

A try-catch statement should have the following form in packetC:

try
{
  statement;
}

catch ( exception )  
{
  statement;
}

General Commentary on Comments

It is expected that packetC code should be clear and understandable. Following the guidelines in this chapter is strongly suggested. Detailed comments should not be needed on a line-by-line basis. As such, comments should not be leveraged to make convoluted code understandable. That said, code written for performance and the low-level nature of packetC applications may drive the need for more comments than are generally seen with C++. packetC code should have enough comments to make a function understandable.

Source code files and each function should begin with a comment header. These headers are intended to describe the functionality, including input and output, without the need to read line-by-line comments. Code following the naming guidelines from this document and indentation guides will be clear and understandable to the reader.

In packetC, both C and C++ style comments are supported. Nesting of the two comment styles is possible. Comment styles should not be nested with the exception of commenting out large sections of code where C style comments are used to start and end the section. This is critical as the pre-processor only supports C style comments and pre-processor macros may affect the construction of the packetC source code provided to the compiler. C++ style comments are built into the packetC compiler. C++ comments must only be used to start a line or following code at a clear indention level.

/* Commenting Out All This Code ===
//
// function header here
int initializeContext ( int context; )
{
  contextInitialized_[context] = true;  // Keep comments aligned
  return true;                          // and do not over-comment.
};
==== End of Commenting Code */

There should be a space between the “//” and the actual comment, and comments should use well written sentences starting with a capitalized first word ending with a period.

Short comments embedded within code should describe what is happening, how it is being done, what parameters mean, and any restrictions or bugs. Short comments should be about what, such as “compute mean value”, rather than how such as “sum of values divided by n” as code and variable names should be clear. These short comments may appear on the same line as the code they describe and should be tabbed over to separate them from comments or may appear on a separate line within the code for longer comments, indented to the same level as the statement they comment.

if (counter > 1)
{
  if ( counter % 3 == 0)                // Test every third one
  {
    // a verbose comment about calculating something important
    very-long-statement-on-this-line;
  }
}

File Comment Headers

Each file should start with a file comment block that gives a short description of what the file contains as well as the functions contained within it. The name of the file must be included in the first line of the body of the comment along with a description of the file's contents. This is followed by a short description of the contents of the file without delineating each function and its role, as each function should have its own function comment header.

The descriptions are followed by an identification of the author or authors of the file as well as a copyright notice. If the file contains code that bears a separate and distinct license, the name of the license and licensor must be highlighted here and the license body placed in a comment block at the end of the file with identification of its presence in the copyright notice portion of the file comment header. In some cases, license agreements may require modification of this approach by the licensor and in those cases the license prevails over the packetC style guidelines. The goal is to keep the packetC file comment header clean and readable whenever possible.

Following the block comment shall be the packet module name declaration, library module name declaration, or shared module name declaration for modules, or the guard macro for included files. Included files must use a guard macro and it is critical to also include a library version definition at the same location when not aided by the support of linker version protections.

The example below shows the recommended form of the file comment header. The comment header uses C++ style comments with a line of stars (“*”) at the beginning and end of the comment header and indentation of descriptions below the three callouts of the filename, author, and copyright notice. The guideline example is shown below:

//==============================================================================
//  time.pc – C-Like time function calls for packetC programmers.
//
//    A series of functions useful in evaluating the current time
//    of day or the actual date when processing is occurring.  In  
//    addition, functions provide formatted textual representations
//    necessary for text based network protocol responses.
//
// author
//    Peder Jungck
//
// copyright notice
//    © 2009 CloudShield Technologies, Inc.
//
//==============================================================================
#ifndef TIME_API_PH
#define TIME_API_PH
#define _TIME_API_VERSION 1.00
  . . .   // body of file here
#endif

Function Comment Headers

Each function should be preceded by a block comment that gives a short description of what the function does and how to use it. Discussion of non-trivial design decisions and side-effects is also appropriate, however, avoid duplicating information clear from the code or called out separately by comment tags distributed throughout the code.

Following the block comment shall be the return value and function name, including the formal parameter list alone on a single line, starting in column 1. As per previous formatting discussions, long function declarations should be clearly indented when spanning multiple lines to avoid confusion. If the return value of a function is a special type that is unique to that function, the type may be declared between the function comment header and the function declaration.

The block comment must identify each parameter in the function declaration and return value along with their function and format of the data they contain. This must be done in the function header and not buried within the function. The corollary to this is that comments should not be strewn throughout the function declaration.

The example below shows the recommended form of the function comment header. The name of the function is not repeated within the function comment header as it should be clean and clear immediately following the comments as all comments regarding the parameters are within the header. As with file comment headers, C++-style comments are used.

//==============================================================================
//  one line description of function's role
//
//  multi-line description of function and general description
//  of what parameters are passed and what the function returns
//
// example usage
//    example-return = function ( example-parameter );
//
// parameters
//    parameter - description of what it does (repeated)
//
// returns
//    value - what it can return
//
//==============================================================================

This standard header must precede every function. The following example is from a sample packetC function:

//==============================================================================
//  Formats the time and returns it in an ASCII string.
//
//  This will return the current system date and time in a 26
//  byte array of type "AsciiTime".  See below for the format
//  that is returned.  The sole parameter accounts for the
//  differing system clock to seconds conversion.
//
// example usage
//    AsciiTime   date = asciiTime(TICKS_PER_SECOND);
//
// parameters
//    ticksPerSecond provides system clock ticks in a second
//
// returns
//    AsciiTime value in the following format:
//
//      "DDD MMM dd hh:mm:ss YYYY"
//
//      DDD   Sun, Mon, Tue, Wed, Thu, Fri, Sat
//      MMM   Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
//      dd    1 to 31 Days
//      hh    0 to 23 Hours
//      mm    0 to 59 Minutes
//      ss    0 to 59 Seconds
//      YYYY  Year
//
//==============================================================================
typedef  byte AsciiTime[26];

AsciiTime asciiTime(int ticksPerSecond)
{
  …

File Naming and Construction Conventions

Within packetC, the naming of files is not a language constraint. You may name any file with whatever name and extension you like. An Integrated Development Environment or other tools may have issues with files named using unexpected extensions or conflicting extensions presumed to be associated with other tools. For example, naming all of your source code files with a .htm or .exe extension may not always work as well as desired, but the #include pre-processor directives won't complain. Table 3-1 provides a set of guidelines for packetC filenames:

images
images

Broader Coding Style Guideline Tips and Techniques

Variables, Types, and Functions

In many C++ style guidelines, the suggestion to avoid named constants is recommended with the suggestion of using functions. The following approach to constants must not be used in packetC.

int FULL_MASK()            // Do Not Use Functions Like This In packetC!
{
  return 255.255.255.255;
};

It is recommended that const int be utilized instead of #define when defining constants to assure type safety.

const int NUM_CONTEXTS = 96;      // do this
#define NUM_CONTEXTS 96           // not this

The C value NULL is not present in packetC as was also made obsolete in C++. Initialized variables and evaluations should expressly leverage zero instead and avoid any emulation of NULL.

const int NULL = 0;               // Don't do this!

Conditional Layout and Form

Loop variables should be initialized immediately before the loop.

doneWithLoop = false;
while (doneWithLoop == false )
{

Loop control statements must be the only variables in the loop statement. If other variable initializations are important to the loop, they must be done prior to the loop and not within the loop control statement.

valuesFound = 0;
for (i = 0; i < 100; i++)  // Warning: Don't introduce other variables
{
   if (xyzzy[i] == TEST_VALUE)
       valuesFound++;
};

Avoid the use of do-while loops since they are less readable than for and while loops. Any do-while loop should have a possible representation in one of these other forms. The conditional at the bottom of a loop is problematic from understanding code as one must go to the bottom of the loop to see when it is true as opposed to seeing the conditional before evaluating what the loop is doing.

Infinite loops should be avoided at all costs as a real-time system getting into an infinite loop can be fatal. In some cases, however, a loop written means other than the controlling loop statement determing terminating the loop. For these forms, use a while (true) controlling loop to clearly highlight that this is an infinite loop.

Loops should not use break and continue as they obscure the actual impact of the control statement.

In C and C++, the use of complex conditional expressions is not recommended. Instead, the suggestion is to try and leverage multiple variables with Boolean evaluation to simplify the statement. For packetC, bool is not a base type and the opposite suggestion is made. Complex statements with clear evaluation and involvement of strictly typed evaluation is critical for performance and accuracy in packetC. The preferred packetC evaluation is as follows:

if ((packetNumber < 0) || (packetNumber > MAX_PACKETS)|| packetNumber == previousPacket)
{

On the other hand, however, in C++ the following is recommended:

bool isOutOfRange = (packetNumber <0) || (packetNumber > MAX_PACKETS);
bool isPrevious = (pcketNumber == previousPacket);
if (isOutOfRange || isPrevious) {

Do not leverage the C++ form as it is dependent on implicit Boolean evaluation and also fights the optimization benefits of the packetC compiler. The following example shows the recommended form in contrast to the traditional C form:

if ( !(bufSize % sizeof(int)) )              // Not recommended

Instead, this should be written to reflect the numeric (not Boolean) nature of the test:

if ( (bufSize % sizeof(int)) == false )     // Recommended, where false == 0.

In addition, the use of explicit tests not only for statements but also in the evaluation of function return-values will help to differentiate between the value true == 1 and a non-zero response that may represent specific meaning based upon the non-zero value returned.

In C, the use of goto statements is considered in conflict with the use of structured code. In packetC, however, tightly tuned code may need the use of a goto statement for achieving optimizations not able to be achieved through other constructs evaluated by the compiler. The use of goto statements in packetC must be used in isolated sections of code and not leveraged to break out of another control statement such as branching out of a switch statement.

Variables, Types, and Functions

Conditionals should not presume a Boolean evaluation of true and instead should test for the value or leverage the integer constants true and false for integer variables being used in Boolean evaluations. Note that const int true = 1; and const int false = 0; are predefined values leveraged for Boolean evaluation in packetC. For example:

if (flagSet) {            //Don't presume non-zero evaluation.
if (flagSet == true) {    // This method validates type and evaluation.

Comments

In packetC, both C and C++ style comments are supported. Nesting of the two comment styles is possible. Comment styles should not be nested with the exception of commenting out large sections of code where C style comments are used to start and end the section.

Within packetC, whitespace and clarity of comments placed throughout the code ensure readability and clarity in what statements in code are doing. Furthermore, file and function comment headers have a strict form that they follow that provides information about what they do. In the real world, however, there is often need for more casual comments that reflect work-in-progress or highlighting code that may raise concerns for one reason or another. These casual comments are not meant to apply to formalized comment blocks required by other company or automated code analysis system inclusions.

These comments, referred to as Gotchas, are used to highlight variables changed out of the normal control flow or other code likely to break during maintenance. In addition, the embedded keywords within these Gotchas are used to point out sections that will need future focus. The form of the Gotchas are such that they can be highlighted within packetC editors as well as identified by robots parsing code in order to make a report that drives targeted effort against a code base. The following list identifies a set of recommended packetC Gotchas where each follows the form:

//   :GOTCHA-NAME: description

where the C++ comment starts at column 1 and the Gotcha is preceded and followed by a colon (“:”) without any spaces between the colons and the Gotcha name. A description, which may follow a specific form for a given Gotcha, follows the :GOTCHA-NAME: on the line. Comments may consist of multiple lines, but the first line should be a self-containing, meaningful summary. The writer's name and the date of the remark should be part of the comment. This information is in the source repository, but it can take a quite a while to find out when and by whom it was added. Often gotchas stick around longer than they should. Embedding date information allows other programmers to make this decision while embedding “who” information lets us know whom to ask.

The recommended standard Gotcha names are:

TODO, BUG, KLUDGE, TRICKY, WARNING and COMPILER

but do constrain yourself from adding more, however, consistent use is key.

  • :TODO: topic
  • Means there's more to do here, don't forget.
  • :BUG: [bugid] topic
  • Means there's a known bug here, explain it and optionally give a bug ID.
  • :KLUDGE:
  • When you've done something ugly highlight it. Optionally explain how you would do it differently.
  • :TRICKY:
  • Tells somebody that the following code is very tricky so don't go changing it without thinking.
  • :WARNING:
  • Beware of something.
  • :COMPILER:
  • Sometimes you need to work around a compiler problem. Document it. The problem may go away eventually.

Some examples follow:

// :TODO: pjj 2009MAR20: possible performance problem
// We should really use a database table here but for now
// using a switch statement.
// :KLUDGE: pjj 2008OCT31: too many gotos and labels
// Needing to get it done quick and couldn't find a good
// conditional statement form.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.234.28