blob: 2fae415c69ddeb561a0a5e825a85be3c8e6212c0 [file] [view]
# Local Conventions For New Code
Use this guide when writing new declarations anywhere in the tree. It records the forms review asks for. It does not
authorize rewriting code the task is not otherwise changing; rule 5 in the repository-root `AGENTS.md` governs
untouched lines. Eigen predates most of these forms, so a tree-wide count is not the convention: new code uses the
current form, and a file being edited heavily should come out uniform rather than half converted.
## Declarations
- Trait and evaluator constants are `static constexpr` members, not `enum` blocks; `enum` constants are being phased
out. Give each the type it is used as: `Flags` is `unsigned int` by convention, predicates are `bool`. In C++14 the
in-class declaration is not a definition: odr-using such a member of a class template — binding it to a `const T&`
parameter such as `numext::mini`'s, or taking its address — links at -O2 and fails at -O0 unless a namespace-scope
`template <...> constexpr T Cls<...>::kName;` definition exists (`arch/Default/Half.h` has the form). Pass a prvalue
(`+kName`, `Index(kName)`) or add the definition; the test suite builds optimized and will not catch the omission.
- Prefer `using` to `typedef`, `nullptr` to `NULL`, `= default` and default member initializers to empty constructor
bodies that assign each member. `using` binds in every tree, `test/` and `unsupported/` included: those were left
out of the sweep that converted `Eigen/src`, so the aliases surrounding new code there are mostly still `typedef`
and matching the neighbours reproduces the form the sweep removed. Do not rely on CI to catch it — the
`modernize-use-using` gap recorded at [`scripts/check_style.py`](../scripts/check_style.py) leaves function-local
typedefs unreported.
- `kCamelCase` is an accepted spelling for `static constexpr` and static constants, alongside the older `snake_case`
and `SCREAMING_CASE` forms. It is not a review finding.
- Use `numext::` math functions rather than `std::` in library code, and Eigen's metaprogramming aliases
(`bool_constant`, `void_t`, `remove_all_t`; see `Eigen/src/Core/util/Meta.h`) rather than spelling out the standard
forms. `internal::is_arithmetic` is not a spelling of `std::is_arithmetic`: it is deliberately specialized for
packet and Eigen scalar types and differs on `long double` during GPU compilation, so use it only when Eigen's
extended arithmetic category is specifically intended.
- Put SFINAE in a defaulted template parameter rather than the return type. When an overload set needs the negative
case too, constrain both overloads: an exact-match overload next to an unconstrained one can bind a converted
temporary and return a dangling reference.
- An in-class definition is already implicitly `inline`; a bare `inline` there is noise. Use `EIGEN_STRONG_INLINE` or
`EIGEN_ALWAYS_INLINE` when inlining matters, and nothing otherwise.
- Compile-time API preconditions use the `EIGEN_STATIC_ASSERT_*` helper that names them (`_VECTOR_ONLY`,
`_SAME_MATRIX_SIZE`, ...) or `EIGEN_STATIC_ASSERT(cond, TOKEN)`. These honor `EIGEN_NO_STATIC_ASSERT` and a
user-provided `EIGEN_STATIC_ASSERT` override. Write unconditional implementation invariants as
`static_assert(cond, "what must hold")`; bare assertions bypass those configuration mechanisms.
- Deprecate, do not remove: mark the old declaration `EIGEN_DEPRECATED` or `EIGEN_DEPRECATED_WITH_REASON("use ...")`,
keep it working by forwarding to the replacement, and name the replacement in its Doxygen block.
- Spell names out (`scratch`, not `scr`) and name traits for the property they assert.
## The C++14 baseline
Supported headers compile as C++14, which rules out forms that review suggestions often reach for:
- `if constexpr` is C++17: use `EIGEN_IF_CONSTEXPR(...)` wherever the condition is compile-time constant. Note the
condition must still be valid C++14 either way — the macro lowers to a plain `if` there.
- Designated initializers are C++20: use aggregate assignment with `/*name=*/` comments. Fields derived from other
fields of the same object must be computed from locals first; the braced temporary cannot read fields it is about
to set.
- `std::span`, CTAD, fold expressions, `constinit`, and later library additions are unavailable outside guarded
backends with a documented newer requirement (the SYCL configurations force C++17, for example).
## Hot paths
Code added to a hot inner loop grows the enclosing function and can displace it from the instruction cache even when
an `EIGEN_PREDICT_FALSE` guard keeps it from executing. Watch for that when adding a check or a fallback to such a
loop; where a benchmark shows the cost, moving the cold path into an `EIGEN_DONT_INLINE` helper is one way to
recover it.
## Comments
The comment rules in the repository-root `AGENTS.md` are enforced in review and are the most repeated style finding
here. Before publishing a diff, reread each added comment and delete the ones that narrate code or restate an
identifier. Keep the ones recording mathematics, invariants, compatibility constraints, provenance, or the reason a
slower or unusual form is deliberate — stated at the construct, not in the merge request.
Prefer the most precise notation that fits. A recurrence, an error bound, an invariant written as an expression, or
two lines of pseudo-code usually carry more than a paragraph and are read faster by this audience:
```cpp
// Bad: the relative error in summing n elements this way is bounded by roughly twice the
// machine epsilon multiplied by the quantity log base two of n over B, plus B, where B is
// the number of elements summed sequentially in each leaf of the tree.
// Good: tree summation, relative error <= ~2*eps*(log2(n/B) + B) for leaf size B.
```
A bound, invariant, or identity stated exactly beats prose that half-carries it (`m` kept in `[1, 2)` says more than
"balanced form"), and a named theorem comes with its statement. Reuse the symbols of the surrounding file and the
cited reference rather than inventing one for a sentence; notation that restates what the code shows is the same
defect as prose that does. Prose remains the tool for a *reason*: why this form and not the obvious one. Comments are
plain text, so write expressions the way the rest of the tree does, not in a markup language that does not render.
## REUSE metadata for new files
Every new source file needs accurate REUSE metadata. Original Eigen code normally uses MPL-2.0; prefer the collective
form when an agent cannot truthfully attribute an individual author:
```cpp
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
```
Use the language's comment syntax. Documentation or assets that should not carry inline tags must be covered precisely
in [`REUSE.toml`](../REUSE.toml); do not add a broad annotation that hides unrelated files. Compatible adapted material
may require a different license expression and attribution, which must be preserved rather than relabeled as MPL-2.0.