User Tools

Site Tools


iotivity_c_coding_standards

The following standards define the standard format of C++ code for the IoTivity project and shall be adhered to except where the standard conflicts with existing standards used within a given project. Above all, be consistent in style within the project.

Style

Standards annotated with [C/C++] apply to both C and C++ code. Standards marked with [C++] apply to C or C++ code that will be compiled with a C compiler. Standards marked with [C] apply to code that will only be compiled with a C compiler.

General Standards

  • All code shall use an indentation width of 4 spaces, and no tabs may be used to indent code. Editors should be configured to provide this standard automatically. When editing code, which does not follow this standard, favor the standard already in use in the existing code, eliminating tabs if possible. [C/C++]
  • Typically, line length should be limited between 80 and 100 characters, however exceptions are allowed for deeply nested loop and conditional constructs (always look for ways to simplify such constructs first). Wrapped lines should continue the current expression with additional indentation to clarify that the expression has been wrapped. Indent such lines logically (i.e. function parameters and logically grouped expressions should align vertically). [C/C++]
  • Code must compile with no warnings at the standard warning level without flags forcing any warnings off. Exceptions are allowed only for warnings that occur within any external SDKs used or warnings that cannot be eliminated without using pragmatics (e.g. for nested STL templates, certain compilers will warn that names have been truncated). For these cases, #pragma directives may be used to eliminate the warning, but only for the module in which the warning occurs. [C/C++]
  • Structure packing should be left at the compiler default value unless structures are declared for specific calls to external SDKs or used for over-the-air protocols. In such cases the #pragma directives should be localized to the required headers and used in a push/pop context (unless that is unsupported by the compiler). [C/C++]
  • Avoid lengthy functions. Break up functions into smaller functional units where possible unless constructing a deeply nested loop which must run under severe time constraints (e.g. in a 3D rendering or 2D filtering scenario). [C/C++]
  • Preprocessor directives should begin on the first column of the line unless they are being explicitly nested. [C/C++]
  • Pointers must not be cast directly to integers except where forced by an API function. In general an integer the size of a pointer (e.g. intptr_t or uintptr_t) can be made available and may be used when absolutely necessary. If you choose to cast pointers to int and ints to pointers provide a comment with the reason. [C/C++]
  • Initialize all variables before they are used. When initializing C-style structures, prefer
    Type var = {0};

    over

    Type var;
    memset(&var, 0, sizeof(var));

    as the former will complain correctly if the initialization is unsupported due to C++ constructor semantics. For consistency maintain this preference in C code as well as C++. [C/C++]

  • Where functionality is available through multiple external sources, the order of preference is the C/C++ standard libraries, a cross platform external library, or a platform specific library. For example, integer types defined in stdint.h (uint8_t, uint16_t, etc.) are preferred over Windows types like BYTE_T or DWORD_T, except where required for platform interface compatibility. Exceptions can be made when not all platforms support the same standard of the C/C++ library. [C/C++]

Comments

  • Comments should be used liberally throughout the code to clarify non-obvious code. [C/C++]
  • For all external API elements, comments on classes, structures, enumerations, member functions and member variable declarations must enumerate all behaviors of the element, including error and exceptional conditions. These comments shall conform to the standards of the current documentation tool, Doxygen. [C/C++] For example:
    /**
     * Assignment operator for ByteBuffer.
     *
     * If the passed in, buffer is owned by its ByteBuffer instance, a copy
     * will be made in this instance, otherwise just the pointer will be
     * copied. The current contents of this ByteBuffer will be freed if
     * this ByteBuffer owns its buffer.
     *
     * @param buffer The buffer to assign to this ByteBuffer.
     *
     * @return A reference to this ByteBuffer instance.
     */
    ByteBuffer &      operator=(const ByteBuffer &buffer);
  • Following the JavaDoc style for multi-line comments helps keep the format consistent across languages. For more usage details see the information on Efficient Doxygen Comments
  • Unused code should not be commented or #ifdef’d out, instead delete the code. [C/C++]
  • Inline comments must be single-line (//) format. Use multi-line comments in a header declarations or at the beginning of a file or method to provide longer documentation. [C/C++]

Naming Conventions

  • Names of functions, variables, classes/structures, enumerations, constants and files must be descriptive and clear. [C/C++]
  • All type names should be mixed case and begin with a capital letter (use of the MS standard CSomethingSomething is allowed). [C/C++]
  • All local variable names shall be lower-camel-case formatted. [C/C++]
  • Member function names must be lower-camel-case except for MS COM methods that are upper-camel-case. [C/C++]
  • Free function names must be upper-camel-case. [C/C++]
  • Static global constant variables should be declared all uppercase and use underscores to delimit words (aka screaming snake case). [C/C++]
  • Namespace names should be upper camel case, however acronyms are allowed to be used and may be capitalized accordingly. [C++]
  • Template parameter names should be mixed case starting with uppercase. [C++]
  • All member variables should begin with ‘m_’ and be lower-camel-case. [C++]
  • Static member variables must be unambiguously marked to differentiate them from instance member variables. This standard recommends prefixing their names with ‘s_’. [C++]
  • Globally scoped variables should be avoided in favor of statically declared member variables. If used, globally scoped variables must be marked to disambiguate them from locally scoped variables. This standard recommends prefixing their names with ‘g_’. [C/C++]
  • Globally scoped variables and functions should be referenced using the global scope resolution operator to avoid future namespace collisions. [C++]
  • Accessors and mutators for member variables declared in a class must follow Java (getVariable() and setVariable()) guidelines. Accessors for boolean state variables may use 'is' but should never make use of double-negatives (isOpen() or isClosed() but never isNotUnavilable()). [C++]
  • Classes used as exceptions should append the name Exception to their class name. [C++]
  • Declare only one variable in each variable declaration. [C/C++]

Examples:

// Class names are upper camel case
class FooBar
{
public:
    // Methods are lower camel case
    // Accessors start with a get/set prefix
    void setProvider(FooProvider *provider);
    // Methods that do not modify object state are declared const
    int getBar() const { return m_bar; }
private:
    // Static constants are all upper cases with words separated by underscores
    static const char XML_FOO_RECORD[];
    // Static member variables (class variables) start with "s_"
    static FooProvider *s_fooProvider;
    // Instance member variables start with "m_" 
    int m_bar;
};
 
// Global variables begin with "g_"
int g_global;
// File scope global variables also begin with "g_"
static int g_global2;
 
// Global constants are all upper case separated by underscores (even if
// they are not static class members)
const char XML_BAR_RECORD[] = "bar";
 
// POD C structs are not classes, so members do not get 'm_'.
struct Point
{
    int x;
    int y;
};
 
// Free functions are upper camel case
void MyFunctionalFunction(int arg1, int arg2);

Indentation, Scope and Spaces

  • Braces for a control statement are placed on the line following the control statement at the same indent level as the control statement. [C/C++] For example:
    if (x < MIN_X)
    {
        handleUnderflow();
        x = MIN_X;
    }

    Braces may only be placed on the same line as the preceding code when used in an initializer and the entire statement fits on the same line. For example,

    class Rectangle
    {
        int m_x;
        int m_y;
        int m_width;
        int m_height;
    };
     
    // ...
     
    Rectangle rect = { RECT_X, RECT_Y, RECT_WIDTH, RECT_HEIGHT };
  • Braces must always surround conditional or loop blocks. [C/C++]
  • Use columnar alignment to enhance readability of various tables, variable declarations, simple switch statements and similar repeated constructs. [C/C++]
  • Spaces must exist between comma-delimited items. Spaces must exist between boolean conditionals. Spaces should exist between the left and right value of an assignment operator. Spaces should otherwise be used to maximize readability. [C/C++]
  • Empty lines should be used to group functionally cohesive blocks of code. [C/C++]

Files

  • All C++ files should use the extension ‘.cpp’. [C++]
  • All header files should use the extension ‘.h’ except where such use would be inconsistent with an existing project. [C/C++]
  • All C files should use the extension ‘.c’, unless they will be compiled exclusively by a C++ compiler. [C]
  • All files except those automatically regenerated by the build process shall include a header block with copyright information. [C/C++]
  • Header files must be guarded against multiple inclusion using #ifndef with a name unique within the project. [C/C++]
  • Except for standard precompiled headers, all C++ modules must include the header containing the declarations for the module first to ensure the header references all #include it requires. [C/C++]
  • Use relative include paths in all projects. Absolute paths, even for SDK includes, are strictly forbidden. [C/C++]
  • All #includes should occur at the beginning of a file. Certain constants and preprocessor definitions are allowed prior to the #includes, only if they affect compilation options for the include files. [C/C++]
  • All filenames must be lowercase and use underscore(s) to separate words, unless a certain filename format is required by the platform. [C/C++]

Const

  • All member functions that do not modify the class or modify only mutable member variables shall be declared const. [C++]
  • All pointers and references passed into functions where the functions do not modify the object or data referenced shall be declared const. [C++]

Best Practices Recommendations

The following guidelines are recommendations for how to avoid problems when developing C++ code.

  • C/C++ switch statements should always be preferred to nested if statements where ordinal values are compared. If a switch statement becomes sufficiently large, consider a mapping table of constants and function pointers to improve lookup time and decrease code size. Assume that a switch statement's conditionals are tested in order. If they are not, ordering them this way is irrelevant. If they are, having the most common cases first will save execution time. A compiler can optimize a switch statement in ways it cannot with nested if statements. [C/C++]
  • Use debug output liberally and provide hooks for test functions where appropriate. [C/C++]
  • Declare local variables as close to first use as possible and within the scope they are used, except where doing so would incur significant performance penalty. For example:

    Good
    int y = 0;
    for(int i = 1; i < 100000 ; ++i)
    {
        y += g(i);
    }

    y is defined as close as possible to first use

    Bad

    int y = 0;
     
    … code that does not use y …
     
    for(int i = 1; i < 100000 ; ++i)
    {
        y += g(i);
    }

    It is less clear what y is when it is first used

    Possibly Bad

    int y = 0;
    for(int i = 1; i < 100000 ; ++i)
    { 
        Foo foo;
        y += foo.g(i);
    }

    Foo’s constructor and destructor are run for each iteration. Avoid when invoking the constructor and/or destructor each time are not required.

    Good

    int y = 0;
    Foo foo;
    for(int i = 1; i < 100000 ; ++i)
    {
        y += foo.g(i);
    }

    Foo’s constructor and destructor is run only once. [C/C++]

Const

  • Mutable variables should be restricted to mutual-exclusion objects and reference counts which may be modified in the class even in a const context. [C++]
  • Avoid the use of const_cast (or the C equivalent) except where required. [C/C++]

Constructors and Destructors

  • Singletons must declare all default and copy constructors protected or private. It is recommended that singleton instances be constructed on the heap and deleted in the reverse order, especially if singleton instances reference one another. Library classes are available to assist in this process. [C++]
  • Use virtual destructors unless declaring a structure that must have a well-defined size or declaring a structure that must be used in a union, declaring a class that acts as a singleton or declaring a class that is never intended to be extended. [C++]
  • For objects that represent data that use pointers as member variables, copy constructors and assignment operators must be defined. If one is defined, both must be defined. Virtual assignment operators should be considered a solution of last-resort. It is recommended that classes without copy semantics declare copy constructors and assignment operators private and provide no definition. Alternatively, cloning semantics may be used for objects that live exclusively on the heap so long as the clone interface is consistent across all objects which use it. In general objects with a clone function should hide their copy constructor and assignment operators. [C++]
  • When in doubt, declare any constructor with a single parameter explicit to avoid any implicit type casts. [C++]

Encapsulation

  • All member variables of classes shall be declared private. Public or protected accessors and mutators should be written as appropriate and made inline if possible (in the source file, if supported). Structure members can be public only if the structure is used in a C context as a single public unit or where variable access time must be tightly controlled. Structures that declare member functions should follow the same conventions as classes. [C++]
  • All class declarations not required to exist in a module's header file should reside in the module's source file. If possible, references and pointers to member variables may be used to allow these declarations to be hidden. The use of templatized pointers may break this rule, but should not be avoided where they otherwise simplify code, just to hide class declarations. [C++]
  • Unless part of a class's external behavior, declare member functions protected in preference to declaring them public. Minimize the use of private functions to those activities of a class that cannot be conceivably extended in a derived class. When in doubt, favor extensibility. [C++]

Polymorphism

  • All interfaces should be declared using pure virtual functions and may be declared with no virtual table if supported by the compiler. Classes mixing pure virtual and virtual, instance or static member functions should be avoided. [C++]
  • Avoid downcasting. Downcasting to determine the type of a derived class or to enforce the type of a derived class is not necessary and indicates that changes need to be made to provide polymorphic functions that provide the same extensibility. Certain uses of downcasting to provide type safety and casting behavior between templates may be acceptable but should be limited. [C++]
  • Avoid passing objects by value where possible. [C++]

Templates

  • Member function templates, partial specialization, and explicit specialization should be avoided if not absolutely necessary due to the presence of cross-compiler inconsistencies if cross-compilation may be an issue. [C++]
  • Care must be taken to avoid excessive code bloat through the use of templates. Use of pointers in STL and partial specializations (if available) can help to alleviate bloat. [C++]

Inheritance and Nesting

  • Multiple inheritance should be avoided unless the inherited classes contain only pure virtual functions. Multiple inheritance occasionally simplifies a class hierarchy significantly and may be used in this situation only if the likelihood of porting the code to another object-oriented language is nonexistent. [C++]
  • All classes, unless related to security restrictions, should be designed with extensibility in mind. Classes which represent simple data objects may have fixed functionality, but such restrictions must be carefully thought through. When in doubt, public and protected member functions that provide class behaviors should be virtual. [C++]
  • Classes passed as data objects exclusively by an owning class should be nested within the owning class. Typedefs may be used to avoid excessively long name resolution expressions. [C++]
  • Enumerations, with few exceptions, should be nested within an owning class. [C++]

Operator Overloading

  • Assignment operators in a derived class must always call the assignment operator for the base class. [C++]
  • If the equality operator is overloaded for a class, always overload the inequality operator to avoid ambiguities. [C++]
  • When overloading the binary arithmetic operators, always overload the parallel assignment operators for those classes for which the assignment operator is naturally overloaded. [C++]
  • Prefer the prefix versions of increment and decrement. [C++]

Exceptions and Errors

  • Exceptions should be used for exceptional conditions, not for normal error handling. Structured exceptions (__try/__except in the MS compilers) should be used for calls to external user callbacks to protect against application failure. C++ exception handling and structured exception handling should not be mixed in the same function due to the code bloat which results. Techniques exist which allow these two exception architectures to coexist peacefully. [C++]
  • Catch exceptions by reference to avoid copy semantics. [C++]
  • Hide error details from the outer function scope only if the details are specifically unimportant to that scope. Otherwise either pass errors verbatim (using an error storage class) or re-throw exceptions. [C++]
  • Store error details of the last operation in a class instance only if the instance is used in a thread-local context exclusively or the class specifically manages thread-local errors. [C++]

Threads

  • Assume all management singletons and resource management classes will be used from multiple threads simultaneously unless such classes are COM objects with specific calling-thread restrictions. [C++]
  • In general follow good resource locking guidelines in order to avoid deadlock scenarios. Always release all mutual exclusion objects in a class prior to calling a callback. Always acquire a lock prior to reading or writing any shared resource or use an interlocked API call. Always acquire all locks in a function prior to using any resource that requires any one of the locks. Always acquire all of the locks in every function in a module/class in the same order as the locks are acquired in every other function in a module/class without exception. Always release locks in the opposite order they were acquired. When releasing any given lock, always release all locks acquired after that lock prior to releasing the given lock without exception. [C/C++]
  • Prefer operating-system provided semaphore objects over boolean flags to communicate signals between threads. Assume that all threads are running on different processors. [C/C++]
  • Assume all data objects with copy semantics will be used from only one thread at a time unless the design requires special behavior from the class. In general threads should be able to copy or take exclusive ownership of a piece of data prior to using it, but care must be taken that either ownership is exclusive or the data is thread-safe. Comment or name thread-safe data objects to note their inherent thread safety. [C++]

General Safety

  • Fall-through in switch statements must be strictly commented whenever used. Its use should be restricted to those places where it greatly simplifies code. [C/C++]
  • Safe versions of various library functions exist. These must be used exclusively. Any function call that invites the possibility of a buffer overrun due to an invalid parameter must be disallowed. [C/C++]
  • Fixed size buffers may be used but must be carefully controlled to disallow buffer overruns. [C/C++]
  • All calls to new or malloc or an equivalent interface function shall be assumed to fail by default. [C/C++]
  • The functions malloc, realloc, and free should be used only sparingly and only if tightly encapsulated in a class. Otherwise new and delete should be used exclusively for heap allocation. Never mix new and free or malloc and delete no matter how the underlying operators are implemented. Even better, in C++11, consider using smart pointers and make_shared (also make_unique in C++14), eliminating explicit allocation entirely. [C++]
  • Pass references instead of pointers wherever possible. When using pointers assume all pointers are passed with the value 0. [C++]
  • Use varargs as little as possible. Prefer std::list or similar list constructs where possible. [C++]
  • Use smart pointers where possible to ensure proper object life (watching for cyclic references). [C++]
  • Avoid catch(…) when possible. [C++]
iotivity_c_coding_standards.txt · Last modified: 2015/03/18 23:12 by Ossama Othman