*[Code formatting and style](#code-formatting-and-style)
## Workflow
ADIOS uses the GitHub fork-and-branch model. In this, the project "lives" in it's main repository located at https://github.com/ornladios/adios2.git, while each individual developer has their own copy of the repo to work in. Changes are then submitted to the main repository via pull-requests made with branches from your fork.
...
...
@@ -115,6 +126,225 @@ $ git push -f
You have now created a pull request (PR) that is pending several status checks before it can be merged. Currently, the only check being performed is for source code formatting and style. In the future, however, the will be a more in depth continuous integration system tied to the pull requests that tests for build and test failures every time a PR is submitted or updated. Once the status checks pass, the PR will be eligible for merging by one of the project maintainers.
## Template implementation separation
The ADIOS C++ classes try to explicitly separate class declarations from their implementation. Typically this is done by having a separate .h and .cpp file, however it get's more complicated when templates are involved. To maintain the distinct separation between definition and implementation, we use explicit instantiation with 4 different source file types:
* ClassName.h
* The main header file containing *only* the class and member declarations with no implementation. This also contains the declarations for explicitly instantiated members.
* ClassName.inl
* A file containing inline function implementations that need to be made public. This is to be included at the bottom of ClassName.h and should *only* contain implementations that need to be made public.
* ClassName.tcc
* A file containing most of the template implementations that can be hidden through explicit instantiation.
* ClassName.cpp
* A file containing the non-template implementations and the explicit instation of any template members.
### Example
Here is an example of a simple class `Foo` with template member functions `Bar1` and `Bar2`
#### Before separation of public and private template implementation
##### Foo.h containing all implementation
```cpp
#ifndef FOO_H_
#define FOO_H_
namespaceadios
{
classFoo
{
public:
Foo()
:m_Bar1Calls(0),m_Bar2Calls(0),m_Bar3Calls(0);
{
}
virtual~Foo()=default;
template<typenameT>
voidBar1()
{
Bar1Helper<T>();
}
template<typenameT>
voidBar2()
{
Bar2Helper<T>();
}
voidBar3()
{
Bar3Helper();
}
private:
template<typenameT>
voidBar1Helper()
{
++m_Bar1Calls;
}
template<typenameT>
voidBar2Helper()
{
++m_Bar2Calls;
}
voidBar3Helper()
{
++m_Bar3Calls;
}
size_tm_Bar1Calls;
size_tm_Bar2Calls;
size_tm_Bar3Calls;
};
}// end namespace adios
#endif // FOO_H_
```
#### After separation of public and private template implementation
In this example, we want to hide the template implementation from the header. We will implement this such that `Bar1` is only callable from the core numeric types, i.e. ints, floats, and complex, while `Bar2` is callable from all types. This will necessitate that `Bar1` and it's helper function is implemented in a .tcc file with explicit instantiation for the allowed types while `Bar2` and it's helper function will need to be inlined in the .inl file to be accessible for all types. We will also use a helper macro ADIOS provides to iterate over the core numeric types for the explicit instantiation of `Bar1`.
##### Foo.h containing only prototypes and explicit instantiation declarations
```cpp
#ifndef FOO_H_
#define FOO_H_
#include"ADIOSMacros.h"
namespaceadios
{
classFoo
{
public:
Foo();
virtual~Foo()=default;
template<typenameT>
voidBar1();
template<typenameT>
voidBar2();
voidBar3();
private:
template<typenameT>
voidBar1Helper();
template<typenameT>
voidBar2Helper();
voidBar3Helper;
size_tm_Bar1Calls;
size_tm_Bar2Calls;
size_tm_Bar3Calls;
};
// Create declarations for explicit instantiations
Note here that Bar1Helper does not need an explicit instantiation because it's not a visible funtion in the callable interface. It's implementaion will be available to Bar1 inside the tcc file where it's called from.
##### Foo.inl containing template implementations that always need to be included
```cpp
#ifndef FOO_INL_
#define FOO_INL_
#ifndef FOO_H_
#error "Inline file should only be included from it's header, never on it's own"
#endif
// No need to include Foo.h since it's where this is include from
namespaceadios
{
template<typenameT>
voidFoo::Bar2()
{
Bar2Helper<T>();
}
template<typenameT>
voidFoo::Bar2Helper()
{
++m_Bar2Calls;
}
}// end namespace adios
#endif // FOO_INL_
```
##### Foo.tcc containing template implementations that should be restricted to only known types
```cpp
#ifndef FOO_TCC_
#define FOO_TCC_
#include"Foo.h"
namespaceadios
{
template<typenameT>
voidFoo::Bar1()
{
Bar1Helper<T>();
}
template<typenameT>
voidFoo::Bar1Helper()
{
++m_Bar1Calls;
}
}// end namespace adios
#endif // FOO_TCC_
```
##### Foo.cpp containing non-template implementations and explicit instantiations definitions for known types.
```cpp
#include"Foo.h"
#include"Foo.tcc"
namespaceadios
{
Foo::Foo()
:m_Bar1Calls(0),m_Bar2Calls(0),m_Bar3Calls(0)
{
}
voidFoo::Bar3()
{
Bar3Helper();
}
voidFoo::Bar3Helper()
{
++m_Bar3Calls;
}
// Create explicit instantiations of existing definitions
ADIOS uses the clang-format tool to automatically enforce source code style and formatting rules. There are various ways to integrate the clang-format tool into your IDE / Code Editor depending on if you use Emacs, Vim, Eclipse, KDevelop, Microsoft Visual Studio, etc. that are a bit outside the scope of this document but a quick google search for "integrate <insert-editor-here> clang-format" should point you in the right direction. However, you can always reformat the code manually by running: