Coding Conventions and Rules
These rules should be followed for all new code. Unfortunately, old code might not adhere to all of these rules.
Identifiers should follow our Naming Conventions.
Do not use language features that are not compatible with C99, C++14, and MSVC v1900+ (Visual Studio 2015). Examples of such features include variable-length arrays. Exceptions are allowed when interfacing with a library which requires a newer standard.
All new code added to SUNDIALS should be formatted with clang-format for C/C++, fprettify for Fortran, cmake-format for CMake, and black for Python. See Style for details.
Spaces not tabs.
Comments should use proper spelling and grammar.
Following the Google Style Guide, TODO comments are used to note code that is “temporary, a short-term solution, or good-enough but not perfect.”
A consistent TODO comment format provides an easy to search for keyword with details on how to get more information. TODO comments should start with
TODOfollowed by a unique identifier, enclosed in parentheses, for the person most knowledgeable about the issue and a brief description of the TODO item. Generally, these comments should be used sparingly and are not a substitute for creating an issue or bug report. When applicable, the comment should include the relevant issue or bug report number.Examples:
/* TODO(DJG): Update to new API in the next major release (Issue #256) */If statements and loops should always have braces even if they are one line.
All SUNDIALS data structures should hold onto a
SUNContextobject. Exceptions are theSUNLoggerandSUNProfilerclasses.All SUNDIALS functions should return a
SUNErrCode. Many older functions do not do this and are exceptions to the rule for backwards compatibility. In addition, internal helper functions may or may-not return aSUNErrCode.All SUNDIALS functions, with the exception of some functions that do not have access to a
SUNContext, should begin with a call toSUNFunctionBegin(). The argument toSUNFunctionBegin()is aSUNContextwhich should come from the first object in the function parameter list that has aSUNContext. This macro is used for error handling and declares a local variable access via the macroSUNCTX_.SUNErrCode N_VLinearCombination_Serial(int nvec, realtype* c, N_Vector* X, N_Vector z) { SUNFunctionBegin(X[0]->sunctx); // Correct int i; sunindextype j, N; realtype* zd=NULL; realtype* xd=NULL; /* invalid number of vectors */ SUNAssert(nvec >= 1, SUN_ERR_ARG_OUTOFRANGE); // ... } SUNErrCode N_VLinearCombination_Serial(int nvec, realtype* c, N_Vector* X, N_Vector z) { int i; sunindextype j, N; realtype* zd=NULL; realtype* xd=NULL; SUNFunctionBegin(X[0]->sunctx); // Incorrect, SUNFunctionBegin should occur as early as possible /* invalid number of vectors */ SUNAssert(nvec >= 1, SUN_ERR_ARG_OUTOFRANGE); // ... } int CVodeGetEstLocalErrors(void *cvode_mem, N_Vector ele) { CVodeMem cv_mem; if (cvode_mem==NULL) { cvProcessError(NULL, CV_MEM_NULL, __LINE__, __func__, __FILE__, MSGCV_NO_MEM); return(CV_MEM_NULL); } cv_mem = (CVodeMem) cvode_mem; SUNFunctionBegin(cv_mem->sunctx); // Correct - this is as early as possible to call SUNFunctionBegin SUNFunctionBegin(ele->sunctx); // Incorrect - cvode_mem is first in the function parameter list // ... }
All references to
SUNContextobjects should be done via theSUNCTX_macro. The only exceptions are functions in theSUNContextclass.All calls to SUNDIALS functions that return a
SUNErrCodeshould have their return value checked with a macro from theSUNCheckCallfamily. These macros are documented in the header filesundials/priv/sundials_errors_impl.handsundials/priv/sundials_mpi_errors_impl.h.SUNCheckCall(N_VLinearCombination(...)); // Correct
Avoid storing the return value and then checking the stored value except when absolutely necessary.
SUNErrCode err; err = N_VLinearCombination(...); SUNCheckCall(err); // Avoid except when absolutely necessary.
All calls to SUNDIALS functions that do not return a
SUNErrCodeshould be followed by checking the last error stored in theSUNContext. The exception to this rule is for internal helper functions. These should not be checked unless they return aSUNErrCode. These checks are done with theSUNCheckLastErrmacros.// Correct (void) N_VLinearSum(...); SUNCheckLastErr(); // Incorrect - we must check for errors in N_VDotProd before calling a second function sunrealtype norm2 = SUNRsqrt(N_VDotProd(...)); SUNCheckLastErr(); // Correct sunrealtype norm2 = N_VDotProd(...); SUNCheckLastErr(); norm2 = SUNRsqrt(norm2);
Programmer errors should be checked with the
SUNAssertmacro, that verifies whether its argument evaluates to “true”, and specifies an error flag otherwise. By programmer errors we mean, for example, illegal inputs such as mismatching dimensions or aNULLvalue for something that should not be.SUNLinearSolver SUNLinSol_Band(N_Vector y, SUNMatrix A, SUNContext sunctx) { SUNFunctionBegin(sunctx); SUNLinearSolver S; SUNLinearSolverContent_Band content; sunindextype MatrixRows; // Correct - check these with SUNAssert SUNAssert(SUNMatGetID(A) == SUNMATRIX_BAND, SUN_ERR_ARG_WRONGTYPE); SUNAssert(SUNBandMatrix_Rows(A) == SUNBandMatrix_Columns(A), SUN_ERR_ARG_DIMSMISMATCH); SUNAssert(y->ops->nvgetarraypointer, SUN_ERR_ARG_INCOMPATIBLE); // ... }
Return statements should not unnecessarily use parentheses. Prefer
return x;toreturn(x);. Note, however, lots of older SUNDIALS source code usesreturn(x);.Always use
sunindextypefor variables that are related to problem dimensions. E.g., use it for the length of a vector, or dimensions of a matrix. The only exception is when interfacing with a third party library requires a different variable type.Conversely, never use
sunindextypefor variables that are not specifically related to the dimensions of a vector, matrix, etc.. E.g., if you have a variable that represents the number of integer “words” allocated in a workspace do not usesunindextypefor it. Instead use the appropriate integer type (e.g.,int64_t) directly. For counters, usesuncountertype.Do not use unsigned integer types except for
size_twhen the value you are storing is a memory size. Unsigned integer types must never be used in parts of the SUNDIALS API that will be interfaced to Fortran since the Fortran standard does not include unsigned integers.Use
sizeof(variable)rather thansizeof(type). E.g.,int a = 1; int array_length = 10; int* array1 = malloc(array_length * sizeof(a)); // Do this int* array2 = malloc(array_length * sizeof(int)); // Don't do this
Do not use anonymous
enums in public header files (the Python interface generator doesn’t like it). Wrap typedef statements in SWIG guards, e.g.// Don't do this typedef enum { ARK_RELAX_BRENT, ARK_RELAX_NEWTON } ARKRelaxSolver; // Do this enum ARKRelaxSolver { ARK_RELAX_BRENT, ARK_RELAX_NEWTON }; #ifndef SWIG typedef enum ARKRelaxSolver ARKRelaxSolver; #endif
Use the print functions, format macros, and output guidelines detailed in Output.
Follow the logging style detailed in Logging.