Difference between revisions of "CodingStandard"

From GnuCash
Jump to: navigation, search
(Finish C format, add sections on the GLib/GObject/Gtk framework and Guile)
(Remove redundant astyle instructions)
 
(21 intermediate revisions by 6 users not shown)
Line 1: Line 1:
= Coding Style =
+
{| class="wikitable" style="margin: auto;"
== C Format ==
+
! scope="row"|Languages
Gnucash is fairly relaxed about code format, but we will periodically run a reformatter called [http://astyle.sourceforge.net/astyle.html Artistic Style] (astyle) over the code to clean it up. To reduce the need for this (it messes up VCS history, particularly "blame"), please format your code as follows.
+
| [[{{PAGENAME}}|English]]
 
+
| [[{{PAGENAME:He/תקן_קידוד}}|עִברִית]]
In general, follow the [http://www.gnu.org/prep/standards/standards.html#Formatting GNU format] '''except''' use four spaces instead of two for the indents, and ''don't'' indent the braces. To summarize, a properly formatted function will look like this:
+
|}
 +
This page describes the standard coding style throughout the gnucash source code.
 +
[[Category:Development]]
 +
= C Format =
 +
Gnucash is fairly relaxed about code format, but we will periodically run a reformatter called [http://astyle.sourceforge.net Artistic Style] (astyle) over the code to clean it up. To reduce the need for this (it messes up VCS history, particularly "blame"), please format your code as follows.
  
 +
In general, follow the [{{URL:Gnu}}prep/standards/standards.html#Formatting GNU format] '''except''' use four spaces instead of two for the indents, and ''don't'' indent the braces. To summarize, a properly formatted function will look like this:
 +
<syntaxhighlight lang="C">
 
   guint
 
   guint
 
   gnc_account_foo (Account *account, gpointer bar)
 
   gnc_account_foo (Account *account, gpointer bar)
Line 16: Line 22:
 
       return salt;
 
       return salt;
 
   }
 
   }
 
+
</syntaxhighlight>
 
Please keep lines under 80 characters. If you need to wrap, line up the function arguments like this:
 
Please keep lines under 80 characters. If you need to wrap, line up the function arguments like this:
 
+
<syntaxhighlight lang="C">
 
   gnc_account_function_with_a_lot_of_paramters (LongTypeName foo, LongerTypeName *bar,
 
   gnc_account_function_with_a_lot_of_paramters (LongTypeName foo, LongerTypeName *bar,
 
                                                 TypeName baz)
 
                                                 TypeName baz)
 
   {
 
   {
 
   }
 
   }
 +
</syntaxhighlight>
 
We don't do the wide separation of names and aligned parameters, so ''don't'' do this:
 
We don't do the wide separation of names and aligned parameters, so ''don't'' do this:
 +
<syntaxhighlight lang="C">
 
   void gnc_account_foo              (Account    *bar,
 
   void gnc_account_foo              (Account    *bar,
 
                                       Split      *baz,
 
                                       Split      *baz,
Line 29: Line 37:
 
   Split *gnc_account_pepper          (Account    *salt,
 
   Split *gnc_account_pepper          (Account    *salt,
 
                                       Transaction *sausage);
 
                                       Transaction *sausage);
 
+
</syntaxhighlight>
 
''Instead, do it this way:''
 
''Instead, do it this way:''
 +
<syntaxhighlight lang="C">
 
   void gnc_account_foo (Account *bar, Split *baz, gpointer waldo);
 
   void gnc_account_foo (Account *bar, Split *baz, gpointer waldo);
 
   Split *gnc_account_pepper (Account *salt, Transaction *sausage);
 
   Split *gnc_account_pepper (Account *salt, Transaction *sausage);
 +
</syntaxhighlight>
 +
Break up long ''if'' statements like this:
 +
<syntaxhighlight lang="C">
 +
if (gtk_tree_path_get_depth ((GtkTreePath *) selected->data) == 1 &&
 +
    get_action_for_path (selected->data, model) == GNCImport_ADD)
 +
</syntaxhighlight>
 +
 +
= C++ Format =
 +
[[C++]] follows the C format guidelines above, with the following modifications:
 +
* Namespaces shall be all lower case, and the outermost one shall be named '''gnc'''.
 +
* Identifier formats:
 +
** '''Typenames''' (i.e. class, struct, enum, and POD type-aliases) shall be camel-cased.
 +
** '''Function''' and '''object''' names shall be lower-case and shall have words separated by underscores.
 +
** '''Preprocessor Macros''' and '''enumeration names''' shall be upper-case with words separated by underscores.
 +
 +
In addition, certain types of variables shall have prefixes denoting their roles:
 +
* '''Member variables''': '''m_'''.
 +
* '''Static member variables''' (a.k.a class variables): '''c_'''.
 +
* '''Static constants''': '''k_'''.
 +
* '''Free static variables''': '''s_'''. Free static variables means statics which are not local and not members of a class or struct, regardless of scope.
 +
 +
Other Formatting:
 +
* Single-line standalone comments can use either C++ style or C style. End-of-line comments should always use C++ style. The preferred style for multi-line comments is currently under discussion.
 +
* Headers and implementation files should be named for the class they declare or implement. In general any file should declare or implement only one class.
 +
 +
== Coding Guidance ==
 +
 +
* Write modern, idiomatic C++ using the new features of C++11; check the CMAKE_CXX_STANDARD setting in CMakeLists.txt to see what features you may use (currently C++17). In particular:
 +
** Prefer curly braces for initializers and <tt>auto</tt> for variable declarations.
 +
** Declare and initialize variables in one statement at the point that they're first used.
 +
** Use templates instead of copying code and especially instead of preprocessor macros.
 +
** Prefer passing references to pointers.
 +
** '''Do not ''ever''''' pass void* (aka gpointer) in C++ code.  Use templates or class hierarchies to enforce type safety.
 +
 +
= Framework =
 +
Gnucash is a [{{URL:GTK}} Gtk+] project. It's design is object-oriented. The current object orientation is implemented mostly with Gnome's [{{URL:Gnome-dev}}gobject/stable/ GObject] C-language framework and makes heavy use of [{{URL:Gnome-dev}}glib/stable/ GLib]. While this is a rich eco-system, it brings with it a huge number of dependencies which makes GnuCash difficult to port to other operating systems. Consequently the developers have decided to migrate all of GnuCash except the GUI to C++. No new GObject or GLib-dependent code should be written; instead use C++, the C++ standard library, and Boost libraries.
 +
 +
 +
= Guile and Scheme =
 +
Gnucash is partly implemented in a [https://groups.csail.mit.edu/mac/projects/scheme/ Scheme] dialect called [{{URL:guile}} Guile]. (It was originally written mostly in Guile, but that implementation was largely replaced with C several years ago.) In particular, the reports system and part of the business system are written in Guile. To support that, most of the core "engine" API is wrapped and accessible from Guile.
 +
 +
Ideally reports and infrastructure code will be coded using a lisp-specific editor such as Emacs. This will facilitate the indenting of parentheses, which will greatly ease readability and maintenance. [https://www.emacswiki.org/emacs/CodingStyle Emacs coding style] is very useful.
 +
 +
Rules for infrastructure code and reports:
  
For the record, the astyle commandline we use is
+
* most reports should be self-contained;
  astyle --indent=spaces=4 --brackets=break --pad-oper  *.[hc]
+
* a function designed for multiple reports may be added into infrastructure code e.g. report-utilities.scm but must be documented properly;
The rationale for the arguments is contained in [http://lists.gnucash.org/pipermail/gnucash-devel/2009-August/026121.html this email].
+
* aim to create adequate tests using [{{URL:SRFI}}srfi-64/srfi-64.html srfi-64];
 +
* aim for a maximum line width of 78.
  
== Framework ==
+
= Graphical User Interface =
Gnucash is a [http://www.gtk.org Gtk+] project. It's design is object-oriented using Gnome's [http://developer.gnome.org/gobject/stable/ GObject] C-language framework.
+
See [[GUI Guidelines‎‎]].
  
Remember that even though it's in C, it's supposed to be object-oriented code, so decompose your problem with objects, not procedurally. Try to keep functions short and well named.  
+
= Python =
 +
The standard is [{{URL:python}}dev/peps/pep-0008/ PEP 8 -- Style Guide for Python Code].
  
Gnucash is also based upon [http://developer.gnome.org/glib/stable/ GLib], a very rich foundation library. Familiarize yourself with GLib's many utilities and data types, ''and use them''. In particular ''do not write a utility for something which GLib already does.''
+
= Automation =
 +
We have from time to time used [https://astyle.sourceforge.net/ Artistic Style] to reformat code. For simple re-indentation
 +
  astyle --indent=spaces=4 --brackets=break --convert-tabs --suffix=none <files>
 +
suffices. More thorough reformatting can be done with  
 +
  astyle --indent-spaces=4 --brackets=break --pad-first-paren-out --pad-header --pad-oper --unpad-paren --align-pointer=name --min-conditional-indent=0 --max-continuation-indent=60 --break-after-logical --max-code-length=79 --keep-one-line-blocks --convert-tabs --suffix=none <files>
 +
That will force a space between operators (like <tt>if</tt> or <tt>while</tt>) and function names and the initial parenthesis and remove all other padding around parentheses, break lines after 79 characters and break long conditionals on a logical operator (<tt>||</tt> or <tt>&&</tt>) with the logical operators at the ends of lines. Using this command will create a lot of changes in most files so it should be used sparingly and in general only before making extensive code changes.
 +
:The rationale for the arguments is contained in [{{ListURL}}/pipermail/gnucash-devel/2009-August/026121.html this email].
  
(Yes, in spite of those fine words, there's a lot of code in Gnucash that doesn't use GLib types, duplicates GLib or GObject functionality, or is procedural instead of object oriented. We'll eventually get that refactored out, but in the meantime, don't make it any worse.)
+
For those who prefer [https://clang.llvm.org/docs/ClangFormat.html clang-format] satisfactory results can be obtained with
 +
  clang-format --style='{BasedOnStyle: GNU, AccessModifierOffset: -4, IndentWidth: 4, IndentExternBlock: NoIndent, PointerAlignment: Left, SortIncludes: Never, BraceWrapping: IndentBraces: false}'
 +
But note that unlike <tt>astyle</tt>, <tt>clang-format</tt> isn't selective about what it formats: If you use the first <tt>astyle</tt> invocation above it will fix indentation and put the curly braces in the right place without touching anything else, but <tt>clang-format</tt> will enforce the entire GNU format spec as modified by the other options.
  
== Guile and Scheme ==
+
If you intend to use either of these on your own contributions you should first run the command on the unmodified files and commit any changes with an appropriate summary, e.g. <tt>"Reformatted with astyle --indent=spaces=4 --brackets=break --convert-tabs"</tt>, to separate the reformatting from the consequential changes, then run <tt>astyle</tt> or <tt>clang-format</tt> again *before* committing a change. Installing <tt>clang-format</tt> also provides <tt>git-clang-format</tt> that can reformat just your changes and can even be set up as a pre-commit hook to automate the formatting.
Gnucash is partly implemented in a [http://groups.csail.mit.edu/mac/projects/scheme/ Scheme] dialect called [http://www.gnu.org/s/guile/ Guile]. (It was originally written mostly in Guile, but that implementation was largely replaced with C several years ago.) In particular, the reports system and part of the business system are written in Guile. To support that, most of the core "engine" API is wrapped and accessible from Guile.
 

Latest revision as of 19:49, 3 October 2023

Languages English עִברִית

This page describes the standard coding style throughout the gnucash source code.

C Format

Gnucash is fairly relaxed about code format, but we will periodically run a reformatter called Artistic Style (astyle) over the code to clean it up. To reduce the need for this (it messes up VCS history, particularly "blame"), please format your code as follows.

In general, follow the GNU format except use four spaces instead of two for the indents, and don't indent the braces. To summarize, a properly formatted function will look like this:

  guint
  gnc_account_foo (Account *account, gpointer bar)
  {
       Split *baz;
       guint salt;
       if (gnc_split_waldo (baz) > 0)
       {
            salt = gnc_split_pepper (baz);
       }
       return salt;
  }

Please keep lines under 80 characters. If you need to wrap, line up the function arguments like this:

  gnc_account_function_with_a_lot_of_paramters (LongTypeName foo, LongerTypeName *bar,
                                                TypeName baz)
  {
  }

We don't do the wide separation of names and aligned parameters, so don't do this:

  void gnc_account_foo               (Account     *bar,
                                      Split       *baz,
                                      gpointer     waldo);
  Split *gnc_account_pepper          (Account     *salt,
                                      Transaction *sausage);

Instead, do it this way:

  void gnc_account_foo (Account *bar, Split *baz, gpointer waldo);
  Split *gnc_account_pepper (Account *salt, Transaction *sausage);

Break up long if statements like this:

if (gtk_tree_path_get_depth ((GtkTreePath *) selected->data) == 1 &&
    get_action_for_path (selected->data, model) == GNCImport_ADD)

C++ Format

C++ follows the C format guidelines above, with the following modifications:

  • Namespaces shall be all lower case, and the outermost one shall be named gnc.
  • Identifier formats:
    • Typenames (i.e. class, struct, enum, and POD type-aliases) shall be camel-cased.
    • Function and object names shall be lower-case and shall have words separated by underscores.
    • Preprocessor Macros and enumeration names shall be upper-case with words separated by underscores.

In addition, certain types of variables shall have prefixes denoting their roles:

  • Member variables: m_.
  • Static member variables (a.k.a class variables): c_.
  • Static constants: k_.
  • Free static variables: s_. Free static variables means statics which are not local and not members of a class or struct, regardless of scope.

Other Formatting:

  • Single-line standalone comments can use either C++ style or C style. End-of-line comments should always use C++ style. The preferred style for multi-line comments is currently under discussion.
  • Headers and implementation files should be named for the class they declare or implement. In general any file should declare or implement only one class.

Coding Guidance

  • Write modern, idiomatic C++ using the new features of C++11; check the CMAKE_CXX_STANDARD setting in CMakeLists.txt to see what features you may use (currently C++17). In particular:
    • Prefer curly braces for initializers and auto for variable declarations.
    • Declare and initialize variables in one statement at the point that they're first used.
    • Use templates instead of copying code and especially instead of preprocessor macros.
    • Prefer passing references to pointers.
    • Do not ever pass void* (aka gpointer) in C++ code. Use templates or class hierarchies to enforce type safety.

Framework

Gnucash is a Gtk+ project. It's design is object-oriented. The current object orientation is implemented mostly with Gnome's GObject C-language framework and makes heavy use of GLib. While this is a rich eco-system, it brings with it a huge number of dependencies which makes GnuCash difficult to port to other operating systems. Consequently the developers have decided to migrate all of GnuCash except the GUI to C++. No new GObject or GLib-dependent code should be written; instead use C++, the C++ standard library, and Boost libraries.


Guile and Scheme

Gnucash is partly implemented in a Scheme dialect called Guile. (It was originally written mostly in Guile, but that implementation was largely replaced with C several years ago.) In particular, the reports system and part of the business system are written in Guile. To support that, most of the core "engine" API is wrapped and accessible from Guile.

Ideally reports and infrastructure code will be coded using a lisp-specific editor such as Emacs. This will facilitate the indenting of parentheses, which will greatly ease readability and maintenance. Emacs coding style is very useful.

Rules for infrastructure code and reports:

  • most reports should be self-contained;
  • a function designed for multiple reports may be added into infrastructure code e.g. report-utilities.scm but must be documented properly;
  • aim to create adequate tests using srfi-64;
  • aim for a maximum line width of 78.

Graphical User Interface

See GUI Guidelines‎‎.

Python

The standard is PEP 8 -- Style Guide for Python Code.

Automation

We have from time to time used Artistic Style to reformat code. For simple re-indentation

 astyle --indent=spaces=4 --brackets=break --convert-tabs --suffix=none <files>

suffices. More thorough reformatting can be done with

 astyle --indent-spaces=4 --brackets=break --pad-first-paren-out --pad-header --pad-oper --unpad-paren --align-pointer=name --min-conditional-indent=0 --max-continuation-indent=60 --break-after-logical --max-code-length=79 --keep-one-line-blocks --convert-tabs --suffix=none <files>

That will force a space between operators (like if or while) and function names and the initial parenthesis and remove all other padding around parentheses, break lines after 79 characters and break long conditionals on a logical operator (|| or &&) with the logical operators at the ends of lines. Using this command will create a lot of changes in most files so it should be used sparingly and in general only before making extensive code changes.

The rationale for the arguments is contained in this email.

For those who prefer clang-format satisfactory results can be obtained with

 clang-format --style='{BasedOnStyle: GNU, AccessModifierOffset: -4, IndentWidth: 4, IndentExternBlock: NoIndent, PointerAlignment: Left, SortIncludes: Never, BraceWrapping: IndentBraces: false}'

But note that unlike astyle, clang-format isn't selective about what it formats: If you use the first astyle invocation above it will fix indentation and put the curly braces in the right place without touching anything else, but clang-format will enforce the entire GNU format spec as modified by the other options.

If you intend to use either of these on your own contributions you should first run the command on the unmodified files and commit any changes with an appropriate summary, e.g. "Reformatted with astyle --indent=spaces=4 --brackets=break --convert-tabs", to separate the reformatting from the consequential changes, then run astyle or clang-format again *before* committing a change. Installing clang-format also provides git-clang-format that can reformat just your changes and can even be set up as a pre-commit hook to automate the formatting.