Skip to content

Commit fc72109

Browse files
committed
dev: Write the new include convention
1 parent 9bf590f commit fc72109

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

development/convention.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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

Comments
 (0)