Gromacs

Allowed C++ Features

    Table of contents
    No headers

     

    C++ has a lot of features, but to keep the source code maintainable and easy to read, we will avoid using some of them in Gromacs code. The basic principle is to keep things as simple as possible. Here's a preliminary list of things to consider. Most of these are not strict rules, but you should have a very good reason for deviating from them.

    • Use namespaces. Everything in libgromacs should be in a gmx namespace. Don't use using in headers except possibly for aliasing some commonly-used names, and avoid file-level blanket using namespace gmx and similar.
    • Use STL, but don't use iostreams. iostreams are slow, and don't always play well with using C stdio routines at the same time, which are used extensively in the current code.
    • Use exceptions for error handling (see separate page on Handling Errors, once it is up-to-date wrt. use of exceptions).
    • Write exception-safe code. All new code has to offer at least the basic or nothrow guarantee to make the above feasible.
      • Use RAII for managing resources (memory, mutexes, file handle, ...).
        • Use smart pointers for memory management. Use mainly boost::scoped_ptr, boost::shared_ptr, or gmx::gmx_unique_ptr::type (the latter maps to either boost::shared_ptr or std::unique_ptr, depending on compiler capabilities).
        • tMPI provides basic mutexes and RAII locks for them; can be extended when the need arises (see #948).
        • gmx::File provides some RAII support, but may need further work or a rewrite as part of #950.
    • Write const-correct code (no const_casts unless absolutely necessary).
    • Use proper enums for variables that can only contain one of a limited set of values. C++ is much better than C in catching errors in such code.
    • When writing a new class, think whether it will be necessary to make copies of that class. If not, declare the copy constructor and the assignment operator as private and don't define them, making any attempt to copy objects of that class fail. If you allow copies, either provide the copy constructor and the assignment operator, or write a clear comment that the compiler-generated ones will do (and make sure that they do what you want).
    • Declare all constructors with one parameter as explicit unless you really know what you are doing. Otherwise, they can be used for implicit type conversions, which can make the code difficult to understand, or even hide bugs that would be otherwise reported by the compiler. For the same reason, don't declare operators for converting your classes to other types without thorough consideration.
    • Avoid using RTTI (run-time type information, in practice dynamic_cast and typeid) unless you really need it. The cost of RTTI is not very high, neither in binary size (which you always pay if you compile with it) nor in execution time (which you pay only if you use it). If your problem seems to require RTTI, think about whether there would be an alternative design that wouldn't. Such alternative designs are often better.
    • Don't use preprocessor defines for other than things directly related to preprocessing. Use templates or inline functions to generate code, and enums or const variables for constants.
    • Don't use non-const references as function parameters. They make it impossible to tell whether a variable passed as a parameter may change as a result of a function call without looking up the prototype. UPDATE: This has been disputed by some, but no action has been taken.
    • Don't use C-style casts; use const_cast, static_cast or reinterpret_cast as appropriate. See the point on RTTI for dynamic_cast.
    • Don't use malloc, and try to limit the use of new as well. Use container classes when appropriate instead of managing the memory everywhere manually.
    • Avoid overloading functions unless all variants really do the same thing, just with different types. Instead, consider making the function names more descriptive.
    • Avoid using default function arguments.
    • Don't overload operators before thorough consideration whether it really is the best thing to do. Never overload &&, ||, or the comma operator, because it's impossible to keep their original behavior with respect to evaluation order.
    • Don't use complex templates, complex template specialization or techniques like SFINAE. If nothing else, they can make the code more difficult to understand.
    • Don't use multiple inheritance. Inheriting from multiple pure interfaces is OK, as long as at most one base class (which should be the first base class) has any code.
    • Don't write excessively deep inheritance graphs. Try to not inherit implementation just to save a bit of coding; follow the principle "inherit to be reused, not to reuse".
    • Don't use Boost, except parts that all developers have agreed to be essential. These parts will be copied to the Gromacs source tree under src/gromacs/boost/. Boost is a nice library, but we don't want to depend on all the template magic inside, which may not work on more exotic compilers. Excessive template use also slows down compilation significantly.
    • Don't include unnecessary headers (see Library Structure).

    Points for discussion:

    • What is the process of proposing additions to the allowed subset of Boost?
    • Do we want to include features from TR1 and/or from the upcoming C++0x standard? Fixed-size integer types (cstdint, cinttypes, corresponding to C99 headers) are one thing that has come up. What's the compiler support for these?
    • The Google C++ Style Guide is probably a good source for additional points to think about. We might not want to follow it point-to-point, but they have a thorough list of guidelines, with justification for why they've made the choices.

    Exception safety (prelimary, should be moved to the error handling page or merged above):

    • Good guideline for when excpetions are appropriate: "Do we want stack unwinding here? Can I afford stack unwinding here?  Is an exception here the exception, not the rule?"
    • For migration of all code to exception safety see Error Handling
    • Function should be commented whether they are exception safe, whether they might throw an exception (even indirect) and if so which exception they might throw
    • It is not legal to call a function which might throw an exception from a legacy function which is not exception safe
    Page last modified 19:23, 28 Jan 2013 by tmurtola