NRT Style Guide

File Organization

  • The NRT Library is mainly split into the nrt/src/ and nrt/include/ directories. These directories are then further split into Core, ImageProc, Robotics, etc. The build system will then create separate shared libraries for each of these directories, e.g. libnrtCore, libnrtImageProc, libnrtRobotics, etc. This segregation will allow users to pick and choose which functionality they care to compile down the line, and will help alleviate the “dependency soup” that plagues big monolithic libraries. We have set up the following file organization scheme to help maintain this segregation as well as to maintain a high level of organization and documentability of the library:
    • Each directory under a shared library should contain files which can be logically grouped together - e.g. the Core subdirectory has subdirectories such as Memory, Geometry, Debugging, Blackboard, etc. Each of these subdirectories contains a group of classes which implement a specific feature.
    • All header files must be named with a .H extension, and should go into the include directory.
      • Header files should be split such that all backend functionality which users don't need do know about should be hidden away in a details subdirectory.
        • In general, such backend functionality can be split into a Helpers file which contains any helper classes and can be included before the body of a class definition, and a Impl implementation file which includes any inline code. For example, the Blackboard class defined in nrt/Core/Blackboard/Blackboard.H relies on some helper classes defined in nrt/Core/Blackboard/details/BlackboardHelpers.H, and has some inline implementation code defined in nrt/Core/Blackboard/details/BlackboardImpl.H. This organization makes it very easy for us to generate user documentation that omits all of the nasty implementation details.
    • All source files should be named with a .C extension, and should go into the src directory following the same relative path as it's .H file.
      • As much code as possible should be moved into .C files so that users don't have to recompile our .H files over and over again. The big exception to this is template code, which must go into .H files.
    • All header and source files should include the boilerplate preamble/license. There's a script called nrt/scripts/fixAllSourceHeaders.sh which can traverse the whole source tree to fix up any files which are missing this, however you should try to just copy it in whenever you create a new file. Also please don't forget to fill out the ”Primary Maintainer For This File” field so we all know who to complain to/congratulate when your code breaks/works.

Indentation Style

We use astyle to ensure uniform indentation styles across our various text editors. A style file is included with NRT, and can be found in nrt/scripts/astylerc. To use this style file, invoke astyle using the –options flag, e.g.

  astyle myfile --options=/path/to/nrt/scripts/stylerc

Vim:

"-------------Essential NRT Style Compliance Settings-------------
 
" Disable old-school vi compatability
set nocompatible
 
" Allow plugins to control our indentation
filetype plugin indent on
 
" Set each auto-indent level to equal two spaces
set shiftwidth=2
 
" Let each tab equal two spaces
set tabstop=2
 
" Make sure vim turns all tabs into spaces
set expandtab
 
" Make vim indent our code properly
set smartindent
 
" Make the maximum line length equal 120
set textwidth=120
 
"-------------Other cool vim tricks-------------
 
" Use a cool menu when autocompleting filenames, commands, etc...
set wildmenu
set wildmode=list:longest
 
" Make vim automatically change directories to the directory of any file you open. 
" This means that when you open a file, then want to open another using :tabe, :o, etc,
" you can just type in the relative path from the file you're currently editing.
set autochdir
 
" When editing the NRT library, it is a total pain when you are editing a .H file in nrt/include/whatever/whatever, 
" and then decide you need to edit the source .C file in the nrt/src/whatever/whatever. This little function will 
" automatically back track in the directory tree for you, find the corresponding .C or .H file, and open it in a new
" tab. 
" To use it, just type ,o (that's a comma, and then a lower-case o). 
function! OpenOther()
  if expand("%:e") == "C"
    exe "tabe" fnameescape(expand("%:p:r:s?src?include?").".H")
  elseif expand("%:e") == "H"
    exe "tabe" fnameescape(expand("%:p:r:s?include?src?").".C")
  endif
endfunction
nmap ,o :call OpenOther()<CR>

Emacs:

Add this to your ~/.emacs:

;; Hide the menu bar
(menu-bar-mode)
 
;; set default line wrap len:
(setq default-fill-column 120)
 
;;LINUM MODE: displays the line number in the left side of buffers.  format is set to accept up to 4 digits line numbers
;;and showing a "|" character between the line number and the buffer content.
(setq linum-format "%4d")
(global-linum-mode 1)
 
;; NRT indentation style for C++ and such
(defun my-c-mode-common-hook ()
  (local-set-key "\C-h" 'backward-delete-char)
  ;; this will make sure spaces are used instead of tabs
  (setq tab-width 4 indent-tabs-mode nil)
  (setq indent-tabs-mode 'nil)
  (setq c-basic-offset 2)
  (c-set-offset 'substatement-open 0)
  (c-set-offset 'statement-case-open 0)
  (c-set-offset 'case-label 0)
  (c-set-offset 'brace-list-open 0)
  (c-set-offset 'access-label -2)
  (c-set-offset 'inclass 4)
  (c-set-offset 'member-init-intro 4)
  ;; include possible ! as comment start string so that indentation starts after it
  (setq comment-start-skip "/\\*+!* *\\|//+ *")
 
  ;; type C-c C-s or C-c C-o while editing to see what other rules to add here...
)
 
(add-hook 'c-mode-hook 'my-c-mode-common-hook)
(add-hook 'c++-mode-hook 'my-c-mode-common-hook)
(add-hook 'perl-mode-hook 'my-c-mode-common-hook)
(add-hook 'cperl-mode-hook 'my-c-mode-common-hook)
(add-hook 'emacs-lisp-mode-hook 'my-c-mode-common-hook)
(add-hook 'nroff-mode-hook 'my-c-mode-common-hook)
(add-hook 'tcl-mode-hook 'my-c-mode-common-hook)
(add-hook 'makefile-mode-hook 'my-c-mode-common-hook)

Eclipse:

...

Coding Standards

Const Correctness

  • Care should be taken to ensure proper const correctness. Check out Marhall Cline's FAQ on the topic if you're feeling rusty.
  • Write const variables using Marshall's “right-to-left” method using a space between each token. E.g.
//All Wrong...
const int *x;
const int &x;
//All Right...
int const & x;
int const * x;
int * const x;
int const * const x;
  • When creating local variables, make them const as much as possible:
void func(float const val)
{
  float const valsquared = val * val;
  // use valsquared (assuming you will not need to modify it)
}
  • For member functions of classes: use const declaration if the function will not modify the internals of the object:
class MyClass {
int const getVal(int const index) const;
};

Caveat: If a member function should semantically be const (e.g., it reads something from your object) but it actually modifies the object, yet not in a significant manner (typical example is that it locks a mutex member variable in your object), then declare the modified variables (e.g., your mutex) as “mutable”, this is preferred to doing a const_cast inside your member function.

  • For arguments of functions: for input arguments, pass by const ref if possible (except for builtin types like int, float, etc which you pass by const value), otherwise (should be very rare) by const value; for output arguments, pass by ref. Example
void splitRectangle(nrt::Rectangle<int> const & inputrect, nrt::Rectangle<int> & outputleft, nrt::Rectangle<int> & outputright);
  • For return values of functions: return a const ref if possible (i.e., you are returning a const ref to something which will not disappear soon, typically use this for accessor functions of your classes, when returning one of the data members of the class), otherwise return by value (non-const). While returning by non-const value may pose dangers, it also allows move semantics. This is particularly important if you return a vector, a string, etc as by returning it by non-const value it will actually be moved (in most cases) as opposed to copied.

Local Variables

  • Local variable names should favor longer more descriptive names over shorter ones. E.g.
//Bad...
for(int i=0; i<c; ++i);
 
//Better...
for(int itemIdx=0; itemIdx<itemCount; ++itemIdx);

Proper use of inline

  • in declarations, do not write inline (e.g., in class declarations). Inline is an implementation detail and people just looking at your interface (declarations) should not be bothered with it.
  • in definitions (implementation of functions), that's the right place to add inline. For template functions, add inline to the same line as the template.

Example:

  template <class T>
  class Stuff
  {
    public:
      void doit();   // NO INLINE HERE IN DECLARATION
  };

  ...
  
  // IN IMPLEMENTATION FILE:
  template <class T> inline
  Stuff<T>::doit()
  { ... }
  

Proper use of virtual

  • The rule for virtual is the opposite as for inline: specify it in your declarations (users of your classes need to know what is virtual and can safely be reimplemented by derived classes), omit it from your definitions (once a function has been declared virtual, it will stay that way).

Capitalization Rules

  • Class names should be written in CamelCase starting with an uppercase character, for example;
class MyCoolClass;
class ImageSegmenter;
  • Variables should be camelCase starting with a lowercase character, for example:
bool isRunning;
size_t arraySize;
  • Member variables should be camelCase starting with the prefix “its”, for example:
class MyClass
{
  size_t itsCounter;
  std::vector<int> itsStorage;
}
  • Function names should be camelCase starting with a lowercase character, for example:
void doSomething(int paramOne);
 
class MyClass
{
  void doSomethingElse(int paramTwo);
}
  • Typedef's which are just simple aliases for types should be CamelCase starting with an uppercase character, for example:
  typedef nrt::Dims<float> FloatingDims;
  • Anything that is the result of any fancy metaprogramming (e.g. static const variables or typedefs set by compile-time checks) should be written in all lowercase with underscores separating the words, for example:
  template<class T1, class T2> 
  struct simple_promotion
  {
    // Even though this is a typedef, we use lowercase and an underscore to emphasize
    // that it is part of some fancy metaprogramming
    typedef decltype(*(T1*)nullptr + *(T2*)nullptr)) promoted_type;
  };
 
  template<class PixType>
  {
    // Even though this is just a boolean, we use lowercase and an underscore to emphasize
    // that it is part of some fancy metaprogramming
    static const bool is_multi_channel = (PixType::numChannels > 1) ? true : false;
  };
styleguide.txt · Last modified: 2011/08/08 11:51 by rand