Development standards
Notes taken from the opencog/HACKING file. See also: Building OpenCog.
All OpenCog projects are currently located in github, here: https://github.com/opencog/
Coding standards
Formatting
Required for code consistency and patch acceptance:
- Indentation:
- For new C++ file use tabs or better smart - also called mixed - tabs as indentation style
- For new file other than C++, choose the predominant indentation style of surrounding files (Python files tend to use whitespaces for instance)
- For existing file, use the predominant indentation of that file, which should be mainly
- Tabs (or smart/mixed tabs)
- 4 whitespaces
- In case the file you edit is a mixture of whitespaces and tabs, then you may correct it using the predominate indentation style
- C++ access specifier keywords (private, protected, public) should not be indented at all
- No indentation is necessary in a namespace block
- use 80 columns max
- use unix-style end-of-lines (all decent win32 editors support it)
- use definition-block brackets on a new line
- For function declarations, use this style:
void Automobile::Automobile()
{
max_speed = 100;
total_wheels = 4;
}
- NOT this style:
void Automobile::Automobile() {
max_speed = 100;
total_wheels = 4;
}
- Use command-block brackets in a style consistent with the file your are editing.
- Some files are written in this style:
if (condition) {
function_one();
function_two;
} else {
function_three();
function_four();
}
- Other files are written in this style:
if (condition)
{
function_one();
function_two;
}
else
{
function_three();
function_four();
}
- use spaces between expression operators, that is
i + 1
instead ofi+1
- use a space between if/while/for and its condition, that is
if (
instead ofif(
- do NOT use spaces between argument and optional values
int foo(int a=1);
instead ofint foo(int a = 1);
- use a space after a comma, thus
bar(a, b)
instead ofbar(a,b)
Multi-word identifiers
Underscore (or even dash, if the language allows it) are preferred over camel case, to separate multiple words in an identifier. For instance use my_identifier
instead of myIdentifier
.
Type classes, as well as generic template types can use CamelCase (starting in uppercase). Like
AtomSpace as;
or
template<typename InputIterator, typename Predicate>
bool any_of(InputIterator, InputIterator, Predicate);
Includes
When including source files from within OpenCog, you should use the following examples as a guide:
// For headers within the same directory:
#include "myheader.h"
// For headers within a sub directory of the current:
#include "subdir/myheader.h"
// For headers in a parent directory, you should
// use the full opencog include path, and ensure that path
// is in the CMakeList.txt include directive...
#include <opencog/util/RandGen.h>
Handle validity test
When check whether a Handle is validity or not please use the following:
if (h) then ...
or
if (h != nullptr) then ...
rather than the following, which still works, but is deprecated:
if (h != Handle::UNDEFINED) then ...
Logical operators
Prefer not
, and
, or
over !
, &&
, ||
respectively.
Documentation
We use Doxygen for generating code documentation.
This allows a variety of markup to be used, and developers of OpenCog should become familiar with the basic markup and types of comment available. Remember, the time spent on in depth documentation pays itself back multiple-folds when it comes to others (and yourself!) revisiting code you wrote a year earlier.
The basic way of documenting a function would be:
/** Brief description of foo.
*
* An optional and more in depth description of foo
*
* @param bar A description of the bar parameter.
* @param bar2 A description of the bar2 parameter.
* @return A description of the return value.
*
* @bug A bug with foo (should really be in the Github issue tracker!)
* @todo something missing from the implementation of foo (this gets
* added to a global todo list, very handy)
*/
int foo(string bar, bool bar2);
As you can see, we use the @ prefix for special commands.
For a full description of the commands available see the list of special commands. There are also various tutorials to be found on Google.
Checking with astyle
To check whether your code complies with our standard, you may use the tool "astyle". Make sure you use the following options:
prompt-$ astyle --indent=spaces=4 --style=linux --indent-labels \ --pad-oper --keep-one-line-statements --convert-tabs \ --indent-preprocessor file.cc prompt-$ diff file.cc file.cc.orig
Vim
If you use the VIM editor, you may add the following line to your ".vimrc" configuration file to automatically setup your editor to use opencog's style when editing a source file from OpenCog's tree:
autocmd BufNewFile,BufReadPost * if match(expand("%:p:h"), "/opencog") >= 0 && &filetype == "cpp" | set ts=4 sw=4 tw=80 ff=unix cindent expandtab | endif
If you wish to use smart-tab there exists a vim plugin
Emacs
If you use Emacs, you may add the following lines to your ".emacs" as well:
(setq-default indent-tabs-mode nil) ;; use spaces instead of tab
(setq-default c-basic-offset 4) ;; set c based language indent to 4
;; open cxxtest files in C++ mode
(setq auto-mode-alist (cons '("\\.cxxtest$" . c++-mode) auto-mode-alist))
In case you decide to use smart-tab instead of 4-space indentation, here's an emacs plugin.
If you want to use CEDET, here is some help. And if you want to use flycheck (prefered over flymake), here is some help.
Eclipse
In Eclipse (with CDT plugin), you can configure its code formatter using: Window->Preferences->C/C++->Code Style->Formatter.
You can either configure it manually, or download and import this style and enable it by default or for opencog projects only (in project preferences).
More info about setting up Eclipse for OpenCog can be found in this page.
GDB
Avoid breakpoints on guile garbage collection
Normally, gdb breakpoints on signals; however, guile uses signals at a high rate to implement garbage collection.
To avoid these annoying breakpoints, add the following in your .gdbinit
file:
#
# Guile garbage collection uses these, ignore them.
#
handle SIGPWR noprint nostop
handle SIGXCPU noprint nostop
Pretty Print OpenCog Objects
In order to print atoms and other opencog structures in gdb add the following in your .gdbinit
file
# This allows to use on poc on all opencog objects.
set overload-resolution on
#
# Print opencog objects. To be printabled the object must overload the
# function oc_to_string in the opencog namespace (it must use
# namespace opencog { ... }, using namespace opencog won't do)..
#
define poc
if $argc == 0
help patom
else
whatis $arg0
printf "%s\n", opencog::oc_to_string($arg0, opencog::empty_string)._M_dataplus._M_p
end
end
document poc
Prints opencog object information.
Syntax: poc <opencog object>
Example:
poc h - Prints the hypergraph corresponding to h
end
#
# Print opencog::AtomSpace
#
define patomspace
if $argc == 0
help patomspace
else
printf "%s", $arg0.to_string()._M_dataplus._M_p
end
end
document patomspace
Prints opencog::AtomSpace information.
Syntax: patomspace <atomspace>
Example:
patomspace as - Prints the whole atomspace as
end
The main printing function is poc
that allows to print any atom or atom container, or generally any useful data structure used by opencog, except the atomspace, for that you must use patomspace
, as to minimize the chances of printing a massing atomspace by accident. If it doesn't print your favorite opencog data structure merely overload oc_to_string
for it (don't forget to place it inside the opencog namespace).
You need to compile your code in Debug
mode in order to use poc
and patomspace
. If you have problems stepping in your code, let us know, I have a fix for it that I'll put out there if people need it.
Print Other Objects
To pretty print other containers you may have a loot at here.
Debugging Scheme objects
Add the following line to .gdbinit
.
add-auto-load-safe-path /usr/local/lib/libguile-2.2.so.1.2.0-gdb.scm
This requires a version of gdb with build-in scheme support.
Debugging Python objects
Install the Python debugging extensions. On Ubuntu install the package python2.7-dbg
. Then, add the following line to .gdbinit
.
add-auto-load-safe-path /usr/lib/debug/usr/bin/python2.7-gdb.py
For more, or instructions for other operating systems, see https://wiki.python.org/moin/DebuggingWithGdb
Git HOWTO
If you are new to git you can practice here.
In the instructions below, please note that upstream is the same thing as https://github.com/opencog/opencog -- its a synonym, and it means the same thing. Likewise, origin is a synonym for https://username@github.com/username/opencog. These two don't have to actually be called 'origin' and 'upstream', but, by convention, they often are. When you push to origin, you are pushing to your own fork of opencog. If you're trying to understand this workflow, read each numbered step along with the numbered notes below.
- If this is the first time you are using git on the machines, follow this steps to configure git for associating your commits with your personal information. You can see the commit log by running
git log
within a repository-
git config --global user.name "Your Full Name"
-
git config --global user.email “an email that you don't mind being public”
-
- Browse to https://github.com/opencog/opencog, login and click Fork. Note that this workflow is for all repositories at https://github.com/opencog
- In a terminal:
-
git clone https://github.com/YOUR-USERNAME/opencog
-
cd opencog
-
git remote add upstream https://github.com/opencog/opencog
-
git config remote.pushdefault origin
# sets the default remote for pushes to be origin -
git config push.default current
# sets the default push behavior to current -
git checkout master
-
git pull upstream master
-
git push origin master
-
git branch my-latest-fix
-
git checkout my-latest-fix
- hack hack hack
-
- On finishing a self-contained work, no matter how small it is,
-
git add file_name_1 file_name_2 ... file_name_n
-
git commit -m "commit message"
-
- On finishing your hack for the day,
-
git push -u origin my-latest-fix
-
- When you believe your work is ready to merged with upstream/master, browse to your personal branch, click Pull Request and wait for your pull request to be reviewed. Create new branches or work with old branches at any time by returning to step 3.6.
- Optionally, after you've finished working with a branch, and after any pull request has been merged or otherwise closed, and you wish to completely delete the branch:
-
git checkout master
-
git branch -d my-latest-fix
-
git push origin :my-latest-fix
-
- Optionally, to work with others' branches, e.g.:
-
git remote add linas https://github.com/linas/opencog
-
git fetch linas
-
git branch compile linas/compile
-
git checkout compile
-
- Optionally, to test pull requests of other contributors locally
-
git config --global alias.pr '!f() { git fetch $1 refs/pull/$2/head:refs/remotes/pr/$2; }; f'
(addition of system wide git alias) -
git pr upstream 804
(804 can be another pull request number) -
git checkout pr/804
- test test test
-
git checkout master
-
git branch -rd pr/804
(clean up)
-
- Optionally, for those who have write access to github repos, to prevent accidental pushs to upstream.
-
git remote set-url --push upstream "to_prevent_accidental_push_to_upstream"
-
Notes (item numbers below correspond to item numbers above)
- Initial machine wide configuration of git that will associate your name and email with all commits.
- Creating your personal fork, out of which you can manage numerous consecutive and/or parallel personal branches. This is a one-off step.
-
- A remote tracked repository in opencog/.git/config is created with the name origin (a git convention). This is a one-off step.
- ...
- A remote tracked repository in opencog/.git/config is created with the name upstream. This is a one-off step.
- as commented above. This is a one-off step.
- as commented above. This is a one-off step.
- Return to this step before creating new branches, and to update your GitHub fork's master branch with the OpenCog master (upstream) branch (next step). It is best to never make commits to your local master, but only ever to branches.
- Keeps your local 'master' branch in sync with OpenCog master branch.
- Push to keep the master branch of your github fork updated with the master branch of opencog/opencog.
- Creates a new local working branch
- Moves to the new branch
- ...
-
- Make as many commits as you like before going on to the next step. Make sure you don't add intermediate compiler outputs, binary files as well as backup files (mostly ending with ~) to your commit. In addition big test-datasets should be commited to https://github.com/opencog/test-datasets
- Use
git commit -m "message"
form only when commit message is less than 50 characters. Format long commits as following: first line contains brief commit description less than 50 characters; next line is empty, rest of comment contains detailed commit description, each line less than 80 characters.
-
- Pull commits from the master branch and play your changes over top. Run this command anytime you want to sync a local branch with the master branch.
- Push your work to your personal branch on Github
- Aka a GitHub PR.
- If you've finished working with a branch, including making changes suggested in Pull Request comments
- Return to this step before deleting branches.
- optionally delete your local copy of the branch.
- optionally delete your GitHub copy of the branch.
Now, you don't have to delete the branch, or wait for the merge; you can keep on working in that branch, and then issue new Github pull requests for new work. This could become a problem only if one of the series of Github pull request was denied for some reason -- then you've got a bunch of work depending on a patch that is being denied. Thus, if you have several logically unrelated tasks, its best to do each in a separate branch. But, if you have a long series of tasks all working on the same thing, and its all more-or-less your current project, it would be very unlikely that anything would be denied, so just keep working in the same branch, issue Github pull requests every now and then (to get your code upstream), and maybe pull from master every now and then, (if you really need to). And, in the meantime, if you spot some other completely unrelated bug that you want to fix, you can always make a new branch (locally, off of master), fix it, push it, and go back to your usual working branch like nothing happened.
Dependencies
All OpenCog dependencies are found in the standard Ubuntu software repositories. If you would like to develop new features and are unsure about your choices with respect to new dependencies (libraries, etc.) please discuss on the mailing list. The use of standard packages for dependencies is a great benefit to the ease of setting up new development environments and to the efficiency of deploying OpenCog. Contributions which do not meet these guidelines require special review and may not be accepted into the master mainline branch on Github.
Patches & Merges
Patches should generally follow LKML (Linux Kernel Mailing List) standards and acceptance criteria. Large or significant changes from new developers should be broken into small revisions, uploaded to a user branch on Github and a Pull Request made for discussion. Small patches may be emailed (ask for an email address to send patches at #opencog on IRC.freenode.net).
Please ensure your code conforms to the #Coding standards.
Help review code changes
The more eyes we have glancing over the commits, the more likely people will conform to code style conventions and more importantly you may catch bugs before they become a problem later!