Menu

DeveloperBeginnerGuide

Abyssary

What you need to know when developing for Charon

(This document is currently under construction. Please help to improve it by submitting changes or contacting the authors!)

Before you start delving into details you should have a rough idea of the tools we use to manage the development process. Since interns at our lab usually hear of these tools for the first time, we will give a short introduction on each of them. In case you know the tools already, please make sure to at least read the rules of usage at the bottom of each paragraph.

Please use around half an hour to read this document carefully! In case you do not like it: let us know. In case you have questions: let us know. In case you have suggestions on how to improve this document: let us know! We want to facilitate your work with charon as much as possible.

Bazaar (BZR)

(Remark: We now use bazaar and launchpad for code hosting. The old sourceforge SVN repository is no longer maintained and will eventually be removed altogether.)

The first question you might ask is where to find the source code.
As we want to be able to integrate your code into our public sources (or we might want to undo some of the changes you applied), we use a so-called version control system (VCS).
A brief introduction what VCS are good for may be found here.

We are using Bazaar as our VCS of choice:
The bazaar user guide can be found here:
First you have to download/install a client. Windows users could use TortoiseBZR.
This will install a Windows Explorer Shell Extension, which means that you can right-click on any folder to gain access to bzr repos.
Linux users may use the bzr command line client , some desktop environments also offer GUI integration.

A very good introduction to bazaar can be found in the wiki of the inkscape project here

As a first step you might want to download our source code. This is called branch in the bzr slang (see [BuildGuide] for details on how to download our sources). Once you have modified the code (or added new files) you can save your changes in the repository which is called a commit. There are local commits (Only the repository on your own hard drive get's changed) or remote commits (the repository on the server or another remote location gets updated). Before you commit or push to a remote repository you should run bzr update to get the latest changes others may have made to the code. Once you have combined (or merged ) their changes with your own and made sure that the code compiles correctly you can upload the merged code.

Rules for Usage of Bazaar

  • Never commit any code that does not compile and link. Only commit working software! (Ideally your code should compile under Linux (Ubuntu) and Windows as well. Charon should also work on MacOSX.)
  • Commit regularly, whenever you got something working. This avoids loss of code, e.g. if you accidentaly delete some file or your computer crashes etc. It also reduces occurrences of conflicts during update and simplifies code-reviewing. (Ideally, each small working feature, bugfix, etc. should be a separate commit.)
  • You don't need to push a local commit directly to the server. You can collect many local commits and once the task you were working on is finished you can upload all of your local commits with a single push command.
  • Whenever you commit, enter a message which describes the changes (Bazaar will ask you to do it). This description should be detailed and exhaustive! Please note that commit messages cannot be changed later. Good messages start with a brief summary line (less than 50 characters) followed by a blank line and then a more thorough description.

CMake

You probably already have some preferences on how you want to develop charon.
On Windows we suggest using Visual Studio C++. A free "Express" version is available online [http://www.microsoft.com/germany/express/products/windows.aspx here]. It suffices for the coding and building of charon, although you'll need the "Professional" version to debug code (See [PluginDebugging] for more information). Students can get it for free via the DreamSpark Project.

Of course, there are many more development environments than this one (e.g. Eclipse or Qt Creator which are platform independent). You are encouraged to use your favourite one! Each of these environments has its own project files (often also called makefiles) which are used to store information on how to compile the project. This includes paths, filenames to external libraries, definitions, compiler switches and so on.

This is what CMake was made for: it is a "meta project description" which can be used to create a project for your favourite build environment. To create a CMake project (CMake file) you just have to create a file named CMakeLists.txt in your source folder. In charon you will find these files in many folders and subfolders. For the time being you do not need to know exactly, how to create or modify such files. You just need to know how to use CMake to create your favourite project file. There are two important concepts you should know. First, there are so-called switches (boolean variables) which are used to enable or disable some charon functions. For example you can choose to use LAPACK (a linear algebra library) or OpenCV (a computer vision library) by turning these switches on or off.
Second, there are variables you sometimes have to set because CMake needs to know a few details about your computer such as paths to libraries. We try to adjust our CMake files such that you need to enter as little information as possible.
In case some info is missing, CMake will ask you to enter it. This is done using the CMake GUI (which shows up when you click the cmake icon in Windows or enter cmake-gui on the linux command line). This GUI shows the options and switches you may modify and informs you about errors/missing values when you click the configure button. Don't forget to *generate you project files using the corresponding button before closing the GUI (CMake will give you a warning if neccessary).

If you want to set up your own CMake project files (i.e. write your CMakeLists.txt files), there is a reference of all CMake commands and some Tutorials in the CMake Wiki.

A simple project using CMake would consist of only one directory with a source file main.cpp to be compiled. To tell CMake what to do, one file called CMakeLists.txt has to be created with the following content:

PROJECT(sampleProject)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_EXECUTABLE(sample main.cpp)

That's all you need. To actually set up the build files for your favorite build tool, use e.g. the CMake-GUI (see above).
During the build, this will compile the file main.cpp into an executable called sample(.exe).
To add additional include directories use the command INCLUDE_DIRECTORIES,
to link against additional libraries, use the command TARGET_LINK_LIBRARIES.

C++

Of course you should know a bit about C++ already. However, we noticed that most folks starting internships in our lab know the syntax of C++, but know very little about preprocessor, compiler and linker. Hence, we will shortly give you a very rough idea as the concept of modules/plugins implies you should know about the difference. So here are some very rough notes on these three topics.

The preprocessor is in some ways similar to a search and replace tool which is run before the compiler starts to look at the C++ code. Every preprocessor command (also called directive) starts with a #-sign. The #include <filename> directive copies the contents of <filename> into the current file, whereas #define A b searches for occurences of A in the whole project and replaces them with b.

The compiler you hopefully know already. It translates pure C++ code into binary code. For each .cpp file (not for .h-files!) it generates a binary object file (.obj/.o) which is not executable.
It is important to distinguish between declarations and definitions in order to understand later what the linker does. A declaration only defines an interface. For example, float func(int i); simply states that there is a function that does something with an integer variable and returns a floating point value. You can also declare a variable double b; without giving a hint on its value. As soon as you give a definition (i.e. an implementation) to a corresponding declaration, you really tell the compiler what code should be executed. For example the definition of the mentioned function might look like this:

float func(int i) {
    return pow((float)i,0.5f);
}

The defintion of the variable b could be:

b = 0.5 ;

When the compiler creates binary code, it goes through all definitions/implementations and translates them to machine code. Every time it stumbles across a call to a function or a reference to a variable, the compiler first looks for a definition. If it does not find one, however, it only searches for a valid declaration. It assumes (without knowing for sure) that somewhere a definition corresponding to the declaration will be found later. If no declaration is found, the compiler will return an error.

Later means that the compiler hopes that the linker will find these missing definitions. First, it just concatenates all object files created by the compiler into a single file wich may be an executable (.exe in Windows, executable file in linux) or a library (static .lib/.a or shared .dll/.so). Assume your code compilation finished, but there were still missing definitions/implementations which the compiler ignored. These can be found in two ways.

In the first case you can add a static library (.a in Linux or .lib in Windows) to your linker command line. This static library is nothing else than a previously compiled object file. It might have been created by some company which wants to hide the source code or it might have been precompiled to save some time. You used the header files which came along the static library and the compiler assumed that the definitions will follow in the form of a binary library file. The linker will now search a definition for each missing declaration. In case none is found or one is defined twice, it will return error messages such as unresolved symbol or unresolved external which just says that the declaration (=symbol/external) cannot be resolved (=a corresponding definition/implementation cannot be found).

In the other case you can use a dynamic link library (Windows, .dll) or shared object (Linux, .so). These files are basically the same as static libraries (.lib/.a files), but the linker does not include them during the build process into the executable. Instead, these files have to reside in the one of the system's paths or the same path of the executable of your code. As the linker cannot know that there might be .so or .dll files which finally resolve the missing definitions, it will inevitably throw errors when your code is being linked and definitions are missing. Therefore, you can tell it indirectly about dynamic libraries by specifying the keyword extern before the declaration, as for example extern void func(int i);. In this case, the linker will ignore the missing definition and continue creating your executable. If you then run the program and the definition cannot be found in one of the many .dll or .so files the operating system has access to, your program will crash at runtime.

Charon heavily uses dynamic libraries as each module(=plugin) is such a file. This enables us to only compile each module once and then copy the resulting dynamic libraries to a folder which can be scanned by tuchulcha. This saves a lot of time and we can easily add, remove and update single modules. You will notice that each module consists of three files: a .h, .cpp and a .hxx file. This is somewhat uncommon because usually you only have a .h and .cpp file. In our case, the .cpp file basically contains only two functions named create and destroy. These functions implements the dynamic library interface. Each module has to export a declaration and definition of this function, as tuchulcha will search for it to learn about the functionality the module provides.
The .h file is a typical header file that contains all declarations of the module. Finally, the .hxx file contains the definitions of the template methods of this module. The file ending is .hxx by convention as most our modules are templated C++ code.

You may use the TemplateGenerator to develop new modules. This tool creates these three files based on a GUI to facilitate the generation of new modules.

Rules for the Usage of C++

  • Closely stick to the [CodingConventions] so that the code remains readable
  • Use Doxygen and the Wiki for writing documentations (see next paragraph)
  • First write a proof-of-concept code that just works in the most important cases and show it to your supervisor
  • Second, write tests, integrate them into the CMake files and then write the documentation while beautifying your source code.
  • In case you do an internship at our lab, both tests and documentation are must-haves to receive your university credit points!

Integrating tests into CMake is easy: Once you have created some executable which performs the test (returning 0 in case of succes, any other value in case of failure), use the ADD_TEST command provided by CMake to tell CMake about the new test.

A simple CMakeLists.txt file which typically would be located in the test subdirectory would look like follows:

IF(BUILD_TESTING)
    ADD_EXECUTABLE(test1 test1.cpp)
    ADD_TEST(NAME test_feature_blah COMMAND test1)
ENDIF(BUILD_TESTING)

Doxygen

Doxygen is tool to automatically create documentations from the source code. Our projects contain a lot more information than just class hierarchies and CMake automatically detects whether you have installed Doxygen. Once the project is generated with CMake you will find a doc sub-project/build target in your development environment which is configured to create these documentations.
The additional information in these docs is generated because our developers work hard to add doxygen-specific comments into their source code. In such a large project, it would be impossible to understand every line of code without it. You are expected to do this as well with your code!

With doxygen, you can add documentation for each class, function or member variables. If some documentation ist still missing, doxygen will give you a warning like "member function blah of class blubb is not documented". In case that there are similar warnings, do not ignore them but add the requested documentation.

Doxygen comments use a special comment mark to be distinguished from usual comments (which you should also use e.g. within your implementations). All doxygen comments have to be located in your .h files, usually the .cpp files are not scanned by doxygen. Doxygen comments start with /// or //! instead of the usual // comment sign, C-Style comments use /* ... */ or /*! ... */ instead of the usual /* */ syntax. Everything within these special comment blocks is parsed by doxygen. Class/Function documentation usually starts with a brief summary (avoid usage of "." within this summary line, a "." is interpreted as end of the brief description) and is follwed by a detailed description. Using the Qt comment style, the brief description is located at a /// block whereas the detailed description resides in a C-Style /** .. */ block. See the Qt link below at the sample Qt class which uses this comments to describe classes and functions.

You basically just can have a look at some source codes to see how it works. It is so simple that you should be able to use it within an hour. Please note that there is the concept of tags, which have a special meaning to doxygen. For example you can use the \todo-tag to mark some piece of code as incomplete, but functional.

Rules of Usage For Doxygen

  • Always document your code. Better first write the documentation and then do the actual coding. We will not accept any undocumented code.
  • Use doxygen parseble comments to describe each class, function and member variable. This documentation should explain what this class/function/member is good for with focus on developers which want to use this class/function/member e.g. as a library. Therefore this should be a general description what happens and does not need to cover every implementation detail. Within your function implementation, use usual (non-doxygen) comments to add documentation with focus on developers which want to understand implementation-details or want to fix a bug within your code. Tricks, hacks etc. shoul be documented there.
  • A good documentation within a function implementation (which would not be parsed by doxygen) is not: "This loop starts at zero and ends at the number of entries in the list". It rather should sound like: "In this loop we collect all triangles which belong to the object by analyzing their circumference". To put it into a nutshell: do not repeat what is standing in the code already. Instead, always try to give additional information, for example by giving an idea of what is being achieved with each part of code.

Have a look at the Doxygen manual for more details on Doxygen and regard our C++ Coding Style .

Qt

In most projects you will not need Qt(pronounced "cute"). This library is basically THE standard for graphical user interfaces (GUI): you can create Windows with buttons, text boxes, sliders and image visualizations, etc. [Tuchulcha] and the TemplateGenerator are both implemented with Qt.
There are a few concepts you might want to know about when starting with Qt. First of all each set of visual components is implemented as a so-called widget which can in turn be inserted into windows. In case you have to implement some kind of GUI please keep in mind that we might want to use your widgets in other scenarios, so please try to keep them general (think for example region of interest selection). To handle events as for example a mouse clicks on a button, Qt uses the notion of signals and slots wich is an implementation of the so called observer pattern. A signal is an event which can be generated by a component (e.g. when you click on a button). This signal can be connected to a slot which basically is a function which you implement to deal with the event. Signals and slots are implemented like any other C++ member function but they reside in special sections called public sigals or public slots (it is also possible to declare them private or protected). A simple Qt class with signals and slots would look like follows:

include <QObject>

/// Simple class example
/** This should demonstrate how to implement classes
*  with signals and slots
*/
class Sample : public QObject {
    Q_OBJECT
public:
    /// some member variable
    int member;

public signals:
    /// sample signal
    /** \param j  new member value */
    void memberChanged(int j);
public slot:
    /// change member
    /** \param j  new member value */
    void setMember(int j);
};

void Sample::setMember(int j) {
    if(j != member) {
        member = j;
        emit memberChanged(j);
    }
}

To be able to use signals/slots, your class needs to inherit QObject and to include the Q_OBJECT peprocessor macro like you see above. This macro adds some code needed for signal/slot management. Delcaration of signals and slots looks like usual function declarations, but note that the return type has to be void. Slots need a function implementation (like setMember above) whereas signals have not to be implemented. The implementation of signals is added automatically by the so called ''meta object compiler'' (MOC). This means, running the moc executable shipped with Qt, a second file is created which has also to be compiled into your executable. If you forget this, you will get undefined references during the link phase.

If you use CMake to set up your project files, there are two CMake commands which will take care of generating the needed MOC files:
QT4_WRAP_CPP
QT4_AUTOMOC
Documentation about these may be found here.

Further Reading

You do not need to learn too much about all these tools, because you can spend arbitrary amount of time to learn everything about them. In case you are interested in details or you need to learn more for some more specific part of your project, here are some starting points:

CMake

C++

Qt


Related

Wiki: BuildGuide
Wiki: CodingConventions
Wiki: PluginDebugging

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.