Skip to content

Bolt Coding Style Guidelines

jayavanth edited this page Apr 18, 2013 · 2 revisions

This document is derived from the Google open source C++ style guide found at: Google Styleguide


v0.2

Version Date Author Change
0.2 1/17/2013 Kent Knox Revised draft based upon team feedback
0.1 1/2/2013 Kent Knox Original draft

The following set of guidelines are the agreed principles the members of the AMD Bolt team have agreed to follow, in writing and contributing code to the open source Bolt project. Any and all code requests to check-in, pull or merge into the Bolt repository should follow the code styles outlined below, at the discretion of the technical lead for the Bolt project.
Since these guidelines were written after over a year’s development on the project, the current code is grandfathered as-is in its current non-conforming state. As development on Bolt continues over the coming months and years, the hope is that all the code will eventually conform to these written guidelines and have 100% conformance. If a developer finds themselves working in a non-conforming file, they should refactor the entire file as they work to achieve conformance. Diff tools normally highlight white space changes and the hope is to reduce the diff noise to one check-in, where all the code shuffling and whitespace changes apply.

Header Files

In general, every .cpp file should have an associated .h file. There are common exceptions, such as unit test files and small .cpp files containing just a main() function.

#define Guard

All header files should have #define guards to prevent multiple inclusions. The format of the symbol name should be H. The #pragma once can optionally be included above the include guards, to help the compiler increase compilation speed, like so:

#pragma once
#ifndef BOLT_CL_TRANSFORM_H_
#define BOLT_CL_TRANSFORM_H_

.inl Files

You may use file names with an .inl suffix to define complex inline functions when needed. Typically all the content in .inl files should be declared in the detail:: namespace, as the contents of these files are typically implementation details.

Names and Order of Includes

Use standard order for readability and to avoid hidden dependencies: C library, C++ library, other libraries' .h, your project's .h

Scoping

Namespaces

Unnamed namespaces in .cpp files are encouraged. With named namespaces, choose the name based on the project, and possibly its path. Do not use a using-directive.

Nonmember, Static Member, and Global Functions

Prefer nonmember functions within a namespace or static member functions to global functions; use completely global functions rarely.

Local Variables

Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.

Static and Global Variables

Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.

Classes

Doing Work in Constructors

Avoid doing complex initialization in constructors (in particular, initialization that can fail or that requires virtual method calls). Constructors should never call virtual functions or attempt to raise non-fatal failures. If your object requires non-trivial initialization, consider using a factory function or Init() method.

Copy Constructors

Provide a copy constructor and assignment operator only when necessary.

Structs vs. Classes

Use a struct only for passive objects that carry data; everything else is a class.

Inheritance

Composition is often more appropriate than inheritance. When using inheritance, make it public.

Multiple Inheritance

Only very rarely is multiple implementation inheritance actually useful. We allow multiple inheritance only when at most one of the base classes has an implementation; all other base classes must be pure interface classes tagged with the Interface suffix.

Access Control

Make data members private, and provide access to them through accessor functions as needed (for technical reasons, we allow data members of a test fixture class to be protected when using Google Test). Typically a variable would be called m_foo and the accessor function foo(). You may also want a mutator function set_foo(). Exception: static const data members need not be private.

Declaration Order

Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.

Write Short Functions

Prefer small and focused functions.

Other C++ Features

##Variable-Length Arrays and alloca() We do not allow variable-length arrays or alloca()

Run-Time Type Information (RTTI)

Avoid using Run Time Type Information (RTTI)

Casting

Use C++ casts like static_cast<>(). Do not use other cast formats like int y = (int)x; or int y = int(x);

Preincrement and Predecrement

Use prefix form (++i) of the increment and decrement operators with iterators and other template objects

Use of const

Use const as much as possible, both for variable and method declarations

Integer Types

Of the built-in C++ integer types, the only one used is int. If a program needs a variable of a different size, use a precise-width integer type from <stdint.h>, such as int16_t

64-bit Portability

Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment

Preprocessor Macros

Be very cautious with macros. Prefer inline functions, enums, and const variables to macros

0 and nullptr/NULL

Use 0 for integers, 0.0 for reals, nullptr (or NULL) for pointers, and '\0' for chars

sizeof

Use sizeof(varname) instead of sizeof(type) whenever possible

auto

Use auto to avoid type names that are just clutter. Continue to use manifest type declarations when it helps readability, and never use auto for anything but local variables. The use of C++11 auto should be restricted to the AMP codebase. The OpenCL codebase should remain compatible with the vs2010 compilers.

Boost

All of Boost is approved to be used in Bolt

C++11

Use only approved libraries and language extensions from C++11 (formerly known as C++0x). Consider portability to other environments before using C++11 features in your project. The use of C++11 auto should be restricted to the AMP codebase. The OpenCL codebase should remain compatible with the vs2010 compilers.

Naming

General Naming Rules

Function names, variable names, and filenames should be descriptive; eschew abbreviation. Types and variables should be nouns, while functions should be "command" verbs

File Names

Filenames should be all lowercase and can include underscores (_), dashes (-) or periods(.). Bolt uses an informal naming scheme based on the internet URL convention; top level domains separated by periods, such as clBolt.test.transform.cpp. This is a descriptive name for the contents of the file, specified in a convention most people are familiar with.

Type Names

Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum

Variable Names

Variable names are all lowercase, with underscores between words. Class member variables have an m_ prefix. For instance: my_exciting_local_variable, m_my_exciting_member_variable

Function Names

Regular functions have mixed case; accessors and mutators match the name of the variable: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable()

Namespace Names

Namespace names are all lower-case, and based on project names and possibly their directory structure: google_awesome_project

Enumerator Names

Enumerators should be named either like constants or like macros: either kEnumName or ENUM_NAME

Macro Names

You're not really going to define a macro, are you? If you do, they're like this: MY_MACRO_THAT_SCARES_SMALL_CHILDREN

Comments

Bolt uses Doxygen style code comments, which are comments that can be pulled out of the code and formatted using an external program. The syntax and style of doxygen comments is documented here: http://www.stack.nl/~dimitri/doxygen/manual/index.html

File Comments

Start each file with license boilerplate, followed by a description of its contents

/***************************************************************************
*   Copyright 2013 Advanced Micro Devices, Inc.
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
***************************************************************************/

In addition, each file should also contain doxygen tags at the top to annotate the file, which is necessary to enable the doxygen processor to parse the file for contents.

/*! \file device_vector.h
 * Header file for the device_container class
 */

Class Comments

Every class definition should have an accompanying comment that describes what it is for and how it should be used The following is an example of the relevant doxygen tags for classes

/*! \brief This defines the AMP version of a device_vector
*   \ingroup Device
*   \details A device_vector is an abstract data type that provides random 
*   access to a flat, sequential region of memory that is performant
*   for the device.  This can imply different memories for different devices.  
*   For discrete class graphics,
*   devices, this is most likely video memory; for APU devices, this can 
*   imply zero-copy memory; for CPU devices, this can imply
*   standard host memory.
*   \sa http://www.sgi.com/tech/stl/Vector.html
*/

Function Comments

Declaration comments describe use of the function; comments at the definition of a function describe operation The following is an example of the relevant doxygen tags for functions

/*! \addtogroup scan
*   \ingroup PrefixSums
*   \{
*   \todo The user_code parameter is not used yet.
*/

/*! \brief inclusive_scan calculates a running sum over a range of values, 
 *   inclusive of the current value.
 *   The result value at iterator position \p i is the running sum of all 
 *   values less than \p i in the input range.
 *
 * \param first The first iterator in the input range to be scanned.
 * \param last  The last iterator in the input range to be scanned.
 * \param result  The first iterator in the output range.
 * \param user_code A client-specified string that is appended to the 
 *  generated OpenCL kernel.
 * \tparam InputIterator An iterator signifying the range is used as input.
 * \tparam OutputIterator An iterator signifying the range is used as output.
 * \return Iterator at the end of result sequence.
 *
 * \code
 * #include "bolt/cl/scan.h"
 *
 * int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 *
 * // Calculate the inclusive scan of an input range, modifying the values 
 * in-place.
 * bolt::cl::inclusive_scan( a, a+10, a );
 * // a => {1, 3, 6, 10, 15, 21, 28, 36, 45, 55}
 *  \endcode
 * \sa http://www.sgi.com/tech/stl/partial_sum.html
 */

Variable Comments

In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required

TODO Comments

Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect Doxygen provides a \todo tag, that it uses to produce a documented page that gathers all of the TODO items into one list

/*!   \todo Need to implement feature X
*/

Deprecation Comments

Mark deprecated interface points with DEPRECATED comments

/*!   \deprecated Feature X is deprecated
*/

Formatting

Line Length

Each line of text in your code should be at most 120 characters long

Non-ASCII Characters

Non-ASCII characters should be rare, and must use UTF-8 formatting

Spaces vs. Tabs

Use only spaces, and indent 2 spaces at a time

Function Declarations and Definitions

If the declaration fits on one line, that is the appropriate style. If it doesn’t fit on one line, use many lines to make in easily readable, with each new line having 1 ‘tabs’

bool retval = DoSomething(argument1, argument2, argument3);
bool retval = DoSomething(
    argument1,
    argument2,
    argument3,
    argument4);

Conditionals

The Bolt style for conditionals and loops is to use squiggly to demark ‘bodies’ of code. This means that every squiggly is on its own line.

if( … )
{
    // code
}
else
{
    // code
}

Loops and Switch Statements

Switch statements may use braces for blocks. Empty loop bodies should use {} or continue

Preprocessor Directives

The hash mark that starts a preprocessor directive should always be at the beginning of the line

Namespace Formatting

The contents of namespaces are not indented