C++ style guide: Difference between revisions

From Octave
Jump to navigation Jump to search
(→‎Namespace: explain reasons why we don't indent namespace)
(Add section for lambda expressions)
 
(61 intermediate revisions by 7 users not shown)
Line 7: Line 7:


== Formatting ==
== Formatting ==
=== Line Length ===
There is no fixed line length.  In general, strive for clarity and readability and use your own judgement.
Everyone has access to monitors with more than 80 columns, but even so, exceptionally long lines can be hard to read.  However, keeping code together on a line that is logically one unit does improve readability.


=== Indentation ===
=== Indentation ===


Use only spaces, and indent 2 spaces at a time.
* Use only spaces, with 2 spaces per indent.
* Tabs are prohibited.


We use spaces for indentation. Absolutely do not use tabs in your code.
==== Functions, class, struct, enum ====
You should probably set your editor to emit spaces when you hit the tab key.


=== Line Length ===
The curly braces defining the beginning and end of the block should appear on their own line.
 
The braces should not be indented, i.e., they align at the same indentation level as the keyword such as {{codeline|class}}.
 
The body of the block is indented.
 
Note that class access specifiers {{codeline|public}}, {{codeline|protected}}, {{codeline|private}} are not indented.
 
Example:
 
<syntaxhighlight lang="cpp">
class MatrixType
{
public:
  enum matrix_type
  {
    Unknown = 0,
    Full,
    Rectangular
  };
 
}
</syntaxhighlight>
 
==== Control structures (if, while, ...) ====
 
When indenting, indent the statement after control structures (like {{codeline|if}}, {{codeline|while}}, etc.).
 
If there is a compound statement, indent ''both'' the curly braces and the body of the statement (so that the body gets indented by ''two'' indents).
 
Example:
 
<syntaxhighlight lang="cpp">
if (have_args)
  {
    idx.push_back (first_args);
    have_args = false;
  }
else
  idx.push_back (make_value_list (args, arg_nm, tmp));
</syntaxhighlight>
 
If you have nested {{codeline|if}} statements, use extra braces for extra clarification.
 
==== Switch statements ====
 
Indent ''both'' the curly braces and the body of the switch statement (so that the body gets indented by ''two'' indents).
 
However, the {{codeline|case}} statement is not doubly indented and instead aligns with the first brace.
 
<syntaxhighlight lang="cpp">
switch (info)
  {
  case -1:
    {
      cout << "function failed\n";
      return false;
    }
 
  case 0:
    return true;
  }
</syntaxhighlight>
 
==== #ifdef directives ====
 
Indent code that follows a conditional processor directive such as {{codeline|#ifdef}} or {{codeline|#else}}.
 
Example
 
<syntaxhighlight lang="cpp">
#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif
</syntaxhighlight>
 
The '#' character may also be placed with the directive rather than remaining in column 1 if this looks better.
 
==== Split long expressions ====
 
Split long expressions in such a way that a continuation line starts
with an operator rather than identifier.  If the split occurs inside
braces, continuation should be aligned with the first char after the
innermost braces enclosing the split.  Example:
 
<syntaxhighlight lang="cpp">
SVD::type type = ((nargout == 0 || nargout == 1)
                  ? SVD::sigma_only
                  : (nargin == 2) ? SVD::economy : SVD::std);
</syntaxhighlight>
 
==== Optional braces ====
 
Consider putting extra braces around a multi-line expression to make it
more readable, even if they are not necessary.  Also, do not hesitate to
put extra braces anywhere if it improves clarity.
 
=== Pointer and Reference appearance ===
 
Declarations of pointers have the '*' character cuddled with the ''name'' of the variable.
 
<syntaxhighlight lang="cpp">
unsigned int *pointer_variable;
</syntaxhighlight>
 
However, references have the '&' character cuddled with the ''type'' of the variable.
 
<syntaxhighlight lang="cpp">
unsigned int& reference_variable;
</syntaxhighlight>
 
=== Miscellaneous ===
 
The negation operator is written with a space between the operator
and its target, e.g., {{codeline|! A}}.
 
=== Function headers ===
 
In general, in non-header files, format function headers like this:
 
<syntaxhighlight lang="cpp">
static bool
matches_patterns (const string_vector& patterns, int pat_idx,
                  int num_pat, const std::string& name)
</syntaxhighlight>
 
The return type of the function and any modifiers are specified on the first
line.  The function name on the second line should start in column 1, and
multi-line argument lists should be aligned on the first character after the open
parenthesis.  Put a space before the left open parenthesis and after
commas, for both function definitions and function calls.
 
For header files, or in class definitions, it may look better not to split the return type from the rest of the function header.  Use your own judgement.
 
=== Class declarations ===
 
The access specifier ({{codeline|public}}, {{codeline|protected}}, {{codeline|private}}) should always be stated rather than relying on the C++ language defaults for a particular object (for example, "{{codeline|class}}" = "{{codeline|private}}").
 
Within a class, the different access blocks should appear in the order 1) {{codeline|public}}, 2) {{codeline|protected}}, 3) {{codeline|private}}.
 
Within an access block, member functions (methods) should be specified before member variables.  If there are both member functions and member variables use
 
    //--------


Keep the length of source lines to 79 characters or less, for maximum
between the sections to visually separate the two categories.
readability in the widest range of environments.  This is inherited from
the [https://www.gnu.org/prep/standards/standards.html#Formatting GNU Coding Standards].


=== Namespace ===
=== Namespace ===


All code should be in the octave namespace (this is an ongoing project, we
All code should be in the {{codeline|octave}} namespace or in a namespace below it.
are still moving existing classes into namespaces but all new classes
 
should go somewhere into octave::).  There is 1 extra level for namespaces
Namespaces should start and stop using the special macros {{codeline|OCTAVE_BEGIN_NAMESPACE(XXX)}} and {{codeline|OCTAVE_END_NAMESPACE(XXX)}}.  There is no indentation of code that is placed into namespaces using these macros.
inside octave to be used with care, we don't want too many namespaces.
 
Ask before creating a new namespace.
Example
 
{{Code|Use of namespace macros|<syntaxhighlight lang="cpp">
OCTAVE_BEGIN_NAMESPACE(octave)
 
OCTAVE_BEGIN_NAMESPACE(math)
 
template <typename T>
void
umfpack_report_control (const double *Control);
 
OCTAVE_END_NAMESPACE(math)
OCTAVE_END_NAMESPACE(octave)
</syntaxhighlight>}}


* Do not indent namespace. The reasons are:
If bare namespace directives must be used, as occasionally is required in Qt code, then the code within the namespace should be indented.
:* simpler diffs
:* no need to break lines that get 2 or 4 extra columns added to the beginning
:* having the opening brace of functions and classes in column 1
:* it may look weird on a small example, but most files will be enclosed in a single namespace anyway.
* Define namespace on the .cc files;
* Do not use "using X" directives;
* Do not declare anything on the std namespace;


{{Code|namespace style on a .h file|<pre>
{{Code|bare namespace usage|<syntaxhighlight lang="cpp">
// Note lack of indentation
// Note indentation and that functions are not defined as "octave::math::foo:foo"
namespace octave
namespace octave
{
{
namespace math
  namespace math
{
  {
    foo::foo (...)
    {
      ...;
    }
  }
}
</syntaxhighlight>}}
 
==== Other Guidelines ====
* Do not use {{codeline|using XXX;}} directives
* Do not declare anything on the {{codeline|std::}} namespace
 
== Naming ==


class foo
Use lowercase names if possible. Uppercase is acceptable for variable names consisting of 1-2 letters. Do not use mixed case (a.k.a. CamelCase) names.
{
public:
  foo (...);


};
=== Member Variables ===


}
Member variables should use the prefix "m_" whenever possible.
}
</pre>}}


{{Code|namespace style on a .cc file|<pre>
=== Class Variables ===
// Note lack of indentation, and that functions are not defined
// as "octave::math::foo:foo"
namespace octave
{
namespace math
{


foo::foo (...)
Class variables should use the prefix "s_" (for "static") whenever possible.
{
  ...;
}


};
=== Filenames ===


}
As with m-files, the file name of a C++ source file containing a class should match the name of the class defined within the file.  For example, "password.h" defines the class "password" rather than "passwd.h" which is a common abbreviation for "password".
}
</pre>}}


== Header Files ==
== Header Files ==
Line 79: Line 228:
=== Order of Includes ===
=== Order of Includes ===


Use the following order with an empty line between each section:
In source files (not headers files), use the following order with an empty line between each section:


# config.h
# config.h
# The C++ wrappers for C headers (cstdlib, cassert, etc.)
# The C++ wrappers for C headers (cstdlib, cassert, etc.)
# C++ standard library headers (iostream, list, map, etc.)
# C++ standard library headers (iostream, list, map, etc.)
# Other POSIX headers (sys/types.h, unistd.h, etc., no need to use {{codeline|#if defined (HAVE_FOO_H)}} if existence is guaranteed because of gnulib)
# Other library header files (glpk.h, curl.h, etc., should be protected by {{codeline|#if defined (HAVE_FOO_H)}} since they may be missing on the build system)
# Other library header files (glpk.h, curl.h, etc., should be protected by {{codeline|#if defined (HAVE_FOO_H)}} since they may be missing on the build system)
# Octave's liboctave headers
# Octave's liboctave headers
# Octave's libinterp headers
# Octave's libinterp headers
# Octave's libgui headers
Other POSIX headers (sys/types.h, unistd.h, etc., should not be included directly into Octave sources.  For portability, use a wrapper function.  Anything you need is probably already available as a wrapper around a function or header provided by gnulib.  See the files in liboctave/wrappers.  This is necessary because although gnulib is great for portability, it does not generally work well with C++.
Similarly, Windows headers should not be included directly into Octave sources.  Wrappers that provide the needed functionality in an OS-independent way should be used instead.  This is almost always possible and there are many examples in the Octave sources.


Each grouping of header files should be alphabetized unless there is some
Each grouping of header files should be alphabetized unless there is some
Line 93: Line 246:
{{Path|oct-parse.in.yy}} and there is a comment in the file for that one.
{{Path|oct-parse.in.yy}} and there is a comment in the file for that one.


== Other C++ features ==
There is a strict ordering of header files/libraries that must be followed.  There are '''no exceptions''' to these rules:
 
* The functions in liboctave/wrappers may only use headers and symbols from gnulib, standard libraries, or OS-dependent libraries.  They '''must not''' use any headers or symbols from other parts of liboctave, libinterp, or libgui.
* liboctave '''must not''' use any headers or symbols from libinterp or libgui.  It must be fully functional without the interpreter or GUI.
* libinterp '''must not''' use any headers or symbols from libgui.  It must be fully functional without the GUI.
 
As much as possible, header files should be independent of other header files.
 
Header files '''must not''' include config.h.  Instead, they should begin by including octave-config.h.
 
Header files should not use any "#if defined (HAVE_FEATURE)" conditionals.  This is not quite true yet, but we are almost there.  '''No new conditionals may be added.'''
 
== C++ features ==
 
=== references ===
 
Use references when passing variables that will be changed by a subroutine rather than the C-style method of passing pointers.
 
{| class="wikitable"
! style="color:green;" | good
! style="color:darkred;" | bad
|-
| <syntaxhighlight lang="c++">
void foo (int& a_ref)
{
  // foo changes content of `a_ref`
  a_ref = a_ref + 1;
}
 
void bar ()
{
  int a = 42;
  foo (a);
}
</syntaxhighlight>
| <syntaxhighlight lang="c++">
void foo (int *a_ptr)
{
  // foo changes content of `a_ptr`
  *a_ptr = *aptr + 1;
}
 
void bar ()
{
  int a = 42;
  foo (&a);
}
</syntaxhighlight>
|}
 
When passing variables that are large, but will not be changed in a subroutine (read-only), use {{codeline|const}} references.  This helps avoid overflowing the finite stack capacity of a program while still ensuring that read-only access is enforced.
 
{| class="wikitable"
! style="color:green;" | good
! style="color:darkred;" | bad
|-
| <syntaxhighlight lang="c++">
void foo (const std::string& str_ref)
{
  // foo does not change content of `str_ref`
}
 
void bar ()
{
  std::string str ("This is a large variable, however as a reference it will take up just 8 bytes on the stack when passed to the subroutine foo()");
  foo (str);
}
</syntaxhighlight>
| <syntaxhighlight lang="c++">
void foo (std::string str_copy)
{
  // foo does not change content of `str_copy`
}
 
void bar ()
{
  std::string str ("This is a large variable that will be copied on to the stack and passed as a temporary variable to the subroutine foo()");
  foo (str);
}
</syntaxhighlight>
|}
 
=== new/delete ===
 
Pointers that will be allocated memory with {{codeline|new}} should be initialized with the C++ literal {{codeline|nullptr}}, not the numerical value 0 or the macro {{codeline|NULL}}.
 
The {{codeline|delete}} keyword accepts {{codeline|nullptr}} and programmers should not put an {{codeline|if (ptr)}} guard around {{codeline|delete}}.
 
{| class="wikitable"
! style="color:green;" | good
! style="color:darkred;" | bad
|-
| <syntaxhighlight lang="c++">
delete ptr;
</syntaxhighlight>
| <syntaxhighlight lang="c++">
if (ptr)
  delete ptr;
</syntaxhighlight>
|}
 
=== lambda expressions ===
 
When capturing variables from the surrounding function, explicitly list the variables being captured rather than relying on a default capture by value (`[=]`) or by reference (`[&]`).  This more clearly captures the programmer's intent and makes the code more understandable.


=== C++11 ===
=== std::string ===


C++11 features are generally allowed. Check if the feature you want to
When an empty string is required, use <code>""</code>, rather than creating an empty
use has been already used.  If not, ask on the mailing list.
string object with <code>std::string ()</code>.


==== auto ====
=== auto ===


Use of {{codeline|auto}} is allowed only where it helps readability
Use of {{codeline|auto}} is allowed only where it helps readability
Line 109: Line 365:
* Beware of copy when using {{codeline|auto}} in for loops. Pass by reference and use {{codeline|const}} unless you're dealing with simple types such as {{codeline|int}}. See [http://lists.gnu.org/archive/html/octave-maintainers/2016-06/msg00144.html 'auto' uses and for-range loops] on the maintainers mailing list for more details.
* Beware of copy when using {{codeline|auto}} in for loops. Pass by reference and use {{codeline|const}} unless you're dealing with simple types such as {{codeline|int}}. See [http://lists.gnu.org/archive/html/octave-maintainers/2016-06/msg00144.html 'auto' uses and for-range loops] on the maintainers mailing list for more details.


=== C++14 ===
=== C++ style casting ===
 
Always use one of the four C++ long style casting forms ({{codeline|static_cast}}, {{codeline|dynamic_cast}}, {{codeline|reinterpret_cast}}, {{codeline|const_cast}}) rather than C-style forms (type cast {{codeline|(new_type) variable}} or the function form {{codeline|new_type (variable)}}).
 
=== C++11 features ===
 
A C++11 compatible compiler is required for [[Building | building Octave]].  Please make use of all C++11 features.
 
=== C++14, C++17, C++20 features ===


Do not use C++14 features.  Octave is widely used in very old systems and we
Try to avoid C++14, C++17, or C++20 features.  Octave is widely used in very old systems and we want them to be able to use up to date versions of Octave.  Building a recent compiler in such systems is not a trivial task so the limitation must happen
want them to be able to use up to date versions of Octave.  Building a recent
compiler in such systems is not a trivial task so the limitation must happen
in Octave.
in Octave.


An exception: code that requires C++14 feature must also implement an
If the implementation using a C++14, C++17, or C++20 feature is very beneficial, make it optional via <code>configure</code> feature detection or also implement an alternative code in the absence of said feature.  In any case, please get in contact with the Octave maintainers on [https://octave.discourse.group/c/maintainers/7 Discourse].
alternative code in the absence of said feature.  In such case, use a
 
configure checkThis increases maintenance a lot, must be used sparsely,
<syntaxhighlight lang="cpp">
and requires approval from other maintainers.
#if defined (HAVE_THIS_C14_FEATURE)
  // code that really needs it
#else
  // alternative code in its absence
#endif
</syntaxhighlight>
 
== Doxygen ==
 
Doxygen documentation can be a great help when developing octave however the current state has a lot of room for improvement. For more information about Doxygen in Octave look at [[Doxygen]].
 
=== Doxygen Style Guide ===
 
Doxygen allows for a variety of commenting styles. In order to maintain uniformity across the entire project the following rules should be applied:
 
* For Doxygen comments use only {{codeline|//!}} and NOT {{codeline|/*! ... */}}, regardless of the size of the block comment
* Use {{codeline|@}} for any [https://www.stack.nl/~dimitri/doxygen/manual/commands.html Doxygen Special Commands]
* Use as little formatting as possibleRestrict to [https://www.stack.nl/~dimitri/doxygen/manual/markdown.html Markdown] and avoid HTML-markup.
* Do NOT use the {{codeline|@brief}} command, the first sentence will automatically be used as the summary description.
* The first sentence should describe briefly what the function does and end with a period.
* Leave a blank line between the Doxygen comment and function definition.
 
An example of properly used Doxygen would look like:
 
<syntaxhighlight lang="cpp">
//! Does something interesting with its arguments.
//!
//! Long comment with **bold** special commands.
//!
//! @param some_param Really should figure out what to do.
//! @param another_param Does something cool with @p some_param.
//!
//! And some example using syntax highlighting:
//!
//! @code{.cc}
//! double v = 1.0;
//! int n = 2;
//! some_function (v, n);
//! @endcode
 
void
some_function (double some_param, int another_param)
{
  // ...
}
</syntaxhighlight>
 
== Comments ==
 
=== FIXME notes ===
 
The preferred comment mark for places that may need further attention is
with {{codeline|FIXME:}} comments.


#if HAVE_THIS_C14_FEATURE
[[Category:Development]]
  // code that really needs it
#else
  // alternative code in its absence
#endif

Latest revision as of 02:18, 20 February 2024

A lot of GNU Octave is written in C++. This document details the C++ style used by the GNU Octave project.

Being part of the GNU project, Octave inherits the GNU coding standards.

See also the GNU Octave Octave style guide for how to write m-files.

Formatting[edit]

Line Length[edit]

There is no fixed line length. In general, strive for clarity and readability and use your own judgement.

Everyone has access to monitors with more than 80 columns, but even so, exceptionally long lines can be hard to read. However, keeping code together on a line that is logically one unit does improve readability.

Indentation[edit]

  • Use only spaces, with 2 spaces per indent.
  • Tabs are prohibited.

Functions, class, struct, enum[edit]

The curly braces defining the beginning and end of the block should appear on their own line.

The braces should not be indented, i.e., they align at the same indentation level as the keyword such as class.

The body of the block is indented.

Note that class access specifiers public, protected, private are not indented.

Example:

class MatrixType
{
public:
  enum matrix_type
  {
    Unknown = 0,
    Full,
    Rectangular
  };

}

Control structures (if, while, ...)[edit]

When indenting, indent the statement after control structures (like if, while, etc.).

If there is a compound statement, indent both the curly braces and the body of the statement (so that the body gets indented by two indents).

Example:

if (have_args)
  {
    idx.push_back (first_args);
    have_args = false;
  }
else
  idx.push_back (make_value_list (args, arg_nm, tmp));

If you have nested if statements, use extra braces for extra clarification.

Switch statements[edit]

Indent both the curly braces and the body of the switch statement (so that the body gets indented by two indents).

However, the case statement is not doubly indented and instead aligns with the first brace.

switch (info)
  {
  case -1:
    {
      cout << "function failed\n";
      return false;
    }

  case 0:
    return true;
  }

#ifdef directives[edit]

Indent code that follows a conditional processor directive such as #ifdef or #else.

Example

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

The '#' character may also be placed with the directive rather than remaining in column 1 if this looks better.

Split long expressions[edit]

Split long expressions in such a way that a continuation line starts with an operator rather than identifier. If the split occurs inside braces, continuation should be aligned with the first char after the innermost braces enclosing the split. Example:

SVD::type type = ((nargout == 0 || nargout == 1)
                  ? SVD::sigma_only
                  : (nargin == 2) ? SVD::economy : SVD::std);

Optional braces[edit]

Consider putting extra braces around a multi-line expression to make it more readable, even if they are not necessary. Also, do not hesitate to put extra braces anywhere if it improves clarity.

Pointer and Reference appearance[edit]

Declarations of pointers have the '*' character cuddled with the name of the variable.

unsigned int *pointer_variable;

However, references have the '&' character cuddled with the type of the variable.

unsigned int& reference_variable;

Miscellaneous[edit]

The negation operator is written with a space between the operator and its target, e.g., ! A.

Function headers[edit]

In general, in non-header files, format function headers like this:

static bool
matches_patterns (const string_vector& patterns, int pat_idx,
                  int num_pat, const std::string& name)

The return type of the function and any modifiers are specified on the first line. The function name on the second line should start in column 1, and multi-line argument lists should be aligned on the first character after the open parenthesis. Put a space before the left open parenthesis and after commas, for both function definitions and function calls.

For header files, or in class definitions, it may look better not to split the return type from the rest of the function header. Use your own judgement.

Class declarations[edit]

The access specifier (public, protected, private) should always be stated rather than relying on the C++ language defaults for a particular object (for example, "class" = "private").

Within a class, the different access blocks should appear in the order 1) public, 2) protected, 3) private.

Within an access block, member functions (methods) should be specified before member variables. If there are both member functions and member variables use

   //--------

between the sections to visually separate the two categories.

Namespace[edit]

All code should be in the octave namespace or in a namespace below it.

Namespaces should start and stop using the special macros OCTAVE_BEGIN_NAMESPACE(XXX) and OCTAVE_END_NAMESPACE(XXX). There is no indentation of code that is placed into namespaces using these macros.

Example

Code: Use of namespace macros
OCTAVE_BEGIN_NAMESPACE(octave)

OCTAVE_BEGIN_NAMESPACE(math)

template <typename T>
void
umfpack_report_control (const double *Control);

OCTAVE_END_NAMESPACE(math)
OCTAVE_END_NAMESPACE(octave)

If bare namespace directives must be used, as occasionally is required in Qt code, then the code within the namespace should be indented.

Code: bare namespace usage
// Note indentation and that functions are not defined as "octave::math::foo:foo"
namespace octave
{
  namespace math
  {
    foo::foo (...)
    {
      ...;
    }
  }
}

Other Guidelines[edit]

  • Do not use using XXX; directives
  • Do not declare anything on the std:: namespace

Naming[edit]

Use lowercase names if possible. Uppercase is acceptable for variable names consisting of 1-2 letters. Do not use mixed case (a.k.a. CamelCase) names.

Member Variables[edit]

Member variables should use the prefix "m_" whenever possible.

Class Variables[edit]

Class variables should use the prefix "s_" (for "static") whenever possible.

Filenames[edit]

As with m-files, the file name of a C++ source file containing a class should match the name of the class defined within the file. For example, "password.h" defines the class "password" rather than "passwd.h" which is a common abbreviation for "password".

Header Files[edit]

Order of Includes[edit]

In source files (not headers files), use the following order with an empty line between each section:

  1. config.h
  2. The C++ wrappers for C headers (cstdlib, cassert, etc.)
  3. C++ standard library headers (iostream, list, map, etc.)
  4. Other library header files (glpk.h, curl.h, etc., should be protected by #if defined (HAVE_FOO_H) since they may be missing on the build system)
  5. Octave's liboctave headers
  6. Octave's libinterp headers
  7. Octave's libgui headers

Other POSIX headers (sys/types.h, unistd.h, etc., should not be included directly into Octave sources. For portability, use a wrapper function. Anything you need is probably already available as a wrapper around a function or header provided by gnulib. See the files in liboctave/wrappers. This is necessary because although gnulib is great for portability, it does not generally work well with C++.

Similarly, Windows headers should not be included directly into Octave sources. Wrappers that provide the needed functionality in an OS-independent way should be used instead. This is almost always possible and there are many examples in the Octave sources.

Each grouping of header files should be alphabetized unless there is some specific reason to not do that. The only case where that is true is in oct-parse.in.yy and there is a comment in the file for that one.

There is a strict ordering of header files/libraries that must be followed. There are no exceptions to these rules:

  • The functions in liboctave/wrappers may only use headers and symbols from gnulib, standard libraries, or OS-dependent libraries. They must not use any headers or symbols from other parts of liboctave, libinterp, or libgui.
  • liboctave must not use any headers or symbols from libinterp or libgui. It must be fully functional without the interpreter or GUI.
  • libinterp must not use any headers or symbols from libgui. It must be fully functional without the GUI.

As much as possible, header files should be independent of other header files.

Header files must not include config.h. Instead, they should begin by including octave-config.h.

Header files should not use any "#if defined (HAVE_FEATURE)" conditionals. This is not quite true yet, but we are almost there. No new conditionals may be added.

C++ features[edit]

references[edit]

Use references when passing variables that will be changed by a subroutine rather than the C-style method of passing pointers.

good bad
void foo (int& a_ref)
{
  // foo changes content of `a_ref`
  a_ref = a_ref + 1;
}

void bar ()
{
  int a = 42;
  foo (a);
}
void foo (int *a_ptr)
{
  // foo changes content of `a_ptr`
  *a_ptr = *aptr + 1;
}

void bar ()
{
  int a = 42;
  foo (&a);
}

When passing variables that are large, but will not be changed in a subroutine (read-only), use const references. This helps avoid overflowing the finite stack capacity of a program while still ensuring that read-only access is enforced.

good bad
void foo (const std::string& str_ref)
{
  // foo does not change content of `str_ref`
}

void bar ()
{
  std::string str ("This is a large variable, however as a reference it will take up just 8 bytes on the stack when passed to the subroutine foo()");
  foo (str);
}
void foo (std::string str_copy)
{
  // foo does not change content of `str_copy`
}

void bar ()
{
  std::string str ("This is a large variable that will be copied on to the stack and passed as a temporary variable to the subroutine foo()");
  foo (str);
}

new/delete[edit]

Pointers that will be allocated memory with new should be initialized with the C++ literal nullptr, not the numerical value 0 or the macro NULL.

The delete keyword accepts nullptr and programmers should not put an if (ptr) guard around delete.

good bad
delete ptr;
if (ptr)
  delete ptr;

lambda expressions[edit]

When capturing variables from the surrounding function, explicitly list the variables being captured rather than relying on a default capture by value (`[=]`) or by reference (`[&]`). This more clearly captures the programmer's intent and makes the code more understandable.

std::string[edit]

When an empty string is required, use "", rather than creating an empty string object with std::string ().

auto[edit]

Use of auto is allowed only where it helps readability and local variables.

  • Never use auto for class members.
  • Do not use auto unless the type really is obscure.
  • Beware of copy when using auto in for loops. Pass by reference and use const unless you're dealing with simple types such as int. See 'auto' uses and for-range loops on the maintainers mailing list for more details.

C++ style casting[edit]

Always use one of the four C++ long style casting forms (static_cast, dynamic_cast, reinterpret_cast, const_cast) rather than C-style forms (type cast (new_type) variable or the function form new_type (variable)).

C++11 features[edit]

A C++11 compatible compiler is required for building Octave. Please make use of all C++11 features.

C++14, C++17, C++20 features[edit]

Try to avoid C++14, C++17, or C++20 features. Octave is widely used in very old systems and we want them to be able to use up to date versions of Octave. Building a recent compiler in such systems is not a trivial task so the limitation must happen in Octave.

If the implementation using a C++14, C++17, or C++20 feature is very beneficial, make it optional via configure feature detection or also implement an alternative code in the absence of said feature. In any case, please get in contact with the Octave maintainers on Discourse.

#if defined (HAVE_THIS_C14_FEATURE)
  // code that really needs it
#else
  // alternative code in its absence
#endif

Doxygen[edit]

Doxygen documentation can be a great help when developing octave however the current state has a lot of room for improvement. For more information about Doxygen in Octave look at Doxygen.

Doxygen Style Guide[edit]

Doxygen allows for a variety of commenting styles. In order to maintain uniformity across the entire project the following rules should be applied:

  • For Doxygen comments use only //! and NOT /*! ... */, regardless of the size of the block comment
  • Use @ for any Doxygen Special Commands
  • Use as little formatting as possible. Restrict to Markdown and avoid HTML-markup.
  • Do NOT use the @brief command, the first sentence will automatically be used as the summary description.
  • The first sentence should describe briefly what the function does and end with a period.
  • Leave a blank line between the Doxygen comment and function definition.

An example of properly used Doxygen would look like:

//! Does something interesting with its arguments.
//!
//! Long comment with **bold** special commands.
//!
//! @param some_param Really should figure out what to do.
//! @param another_param Does something cool with @p some_param.
//!
//! And some example using syntax highlighting:
//!
//! @code{.cc}
//! double v = 1.0;
//! int n = 2;
//! some_function (v, n);
//! @endcode

void
some_function (double some_param, int another_param)
{
  // ...
}

Comments[edit]

FIXME notes[edit]

The preferred comment mark for places that may need further attention is with FIXME: comments.