|
| 1 | +# Coding convention |
| 2 | + |
| 3 | +## Headers |
| 4 | + |
| 5 | +Pinocchio has 4 kinds of headers: |
| 6 | +- Public headers: `xxx.hpp` |
| 7 | +- Private headers: |
| 8 | + - Declaration or declaration and definition when not spited: `xxx.hxx` |
| 9 | + - Definition: `xxx-def.hxx` |
| 10 | + - Explicit template instantiation: `xxx-inst.hxx` |
| 11 | + |
| 12 | +Since Pinocchio is mostly a header only library, include cycles are easy to create. |
| 13 | + |
| 14 | +As a trivial example: |
| 15 | + |
| 16 | +`a.hpp`: |
| 17 | +``` |
| 18 | +#pragma once |
| 19 | +#include "b.hpp" |
| 20 | +
|
| 21 | +struct A |
| 22 | +{ |
| 23 | + B* b; |
| 24 | +}; |
| 25 | +``` |
| 26 | + |
| 27 | +`b.hpp`: |
| 28 | +``` |
| 29 | +#pragma once |
| 30 | +#include "a.hpp" |
| 31 | +
|
| 32 | +struct B |
| 33 | +{ |
| 34 | + A* a; |
| 35 | +}; |
| 36 | +``` |
| 37 | + |
| 38 | +If we build a file that include `a.hpp` we will have the following error: |
| 39 | +```bash |
| 40 | +b.hpp:5:3: error: ‘A’ does not name a type |
| 41 | +``` |
| 42 | +Since `a.hpp` is already included, the guard prevent to include it again |
| 43 | +in `b.hpp` and `A` is never defined. |
| 44 | +This kind of error can be really tricky to debug, |
| 45 | +especially in header only libraries that can have big include chain. |
| 46 | + |
| 47 | +To prevent this we apply the following rule: |
| 48 | +- Private header can't include anything |
| 49 | +- Public header can include private header |
| 50 | + |
| 51 | +Public headers will have the duty to include anything needed by private ones it exposes. |
| 52 | +This will prevent cycles since transitive includes are now forbidden. |
| 53 | + |
| 54 | +This approach have two drawbacks: |
| 55 | +- Transitive include allow to save some code, we will have a lot of redundant code in public headers |
| 56 | +- In private headers, language server will know no symbols (since the dependencies are not included) |
| 57 | + |
| 58 | +To overcome the second issue, we can create the following code snippet to add after the guard of any private header: |
| 59 | +```cpp |
| 60 | +#ifdef PINOCCHIO_LSP |
| 61 | +#include "xxx.hpp" |
| 62 | +#endif // PINOCCHIO_LSP |
| 63 | +``` |
| 64 | +Where `xxx.hpp` is the public header that expose this private header. |
| 65 | + |
| 66 | +By setting the following `.clangd` file at the project root the LSP will be able to see the private header dependencies: |
| 67 | +```yaml |
| 68 | +If: |
| 69 | + PathMatch: .*\.hxx |
| 70 | +CompileFlags: |
| 71 | + Add: |
| 72 | + - -DPINOCCHIO_LSP |
| 73 | +``` |
| 74 | +
|
| 75 | +### Guard |
| 76 | +
|
| 77 | +Pinocchio doesn't use `pragma once` as a guard. Instead, we use the file path converted into snake_case. |
| 78 | + |
| 79 | +As an example `include/pinocchio/spatial/se3.hpp` will have the following guard: |
| 80 | +```cpp |
| 81 | +#ifndef __pinocchio_spatial_se3_hpp__ |
| 82 | +#define __pinocchio_spatial_se3_hpp__ |
| 83 | +
|
| 84 | +// ... |
| 85 | +
|
| 86 | +#endif // ifndef __pinocchio_spatial_se3_hpp__ |
| 87 | +``` |
0 commit comments