$darkmode
Elektra 0.11.0
Including Headers

Problem

In C you can include a header file with

#include "header.h"

or

#include <header.h>

In the C standard, section 6.10.2, paragraphs 2 to 4 state:

  1. A preprocessing directive of the form
#include <h-char-sequence> new-line

searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the < and > delimiters, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.

  1. A preprocessing directive of the form
#include "q-char-sequence" new-line

causes the replacement of that directive by the entire contents of the source file identified by the specified sequence between the " delimiters. The named source file is searched for in an implementation-defined manner. If this search is not supported, or if the search fails, the directive is reprocessed as if it read

#include <h-char-sequence> new-line

with the identical contained sequence (including > characters, if any) from the original directive.

  1. A preprocessing directive of the form
#include pp-tokens new-line

(that does not match one of the two previous forms) is permitted. The preprocessing tokens after include in the directive are processed just as in normal text. (Each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens.) The directive resulting after all replacements shall match one of the two previous forms. The method by which a sequence of preprocessing tokens between a < and a > preprocessing token pair or a pair of " characters is combined into a single header name preprocessing token is implementation-defined.

Definitions:

  • h-char: any member of the source character set except the new-line character and >
  • q-char: any member of the source character set except the new-line character and "

In short, the whole search process of the #include mechanism is implementation-defined. However, because #include works (mostly) like a literal copy-paste, deciding between "" and <> within a public header file can affect user code.

Assumptions

Considered Alternatives

Decision

The rules for including headers are:

Only build/include, which is a copy of src/include after CMake processing, is added to the include path. The entire build/include/elektra directory is installed as-is, with the exact same directory structure and without any further processing.

This is enough for #include <>s to work.

But we will create an additional script that enforces that internal/* headers are not included from headers in src/include/elektra. We will also enforce that the path in a #include "" always starts with a ./ and does not contain any /../.

These include-rules do not apply to tests. Tests can include anything (including .c files) from anywhere within the code base to allow testing private APIs.

Rationale

The decision highlights the difference between installed and non-installed headers. The main driving factor for using "" at all was that including a non-installed private header with <> would be unexpected, since non-installed headers shouldn't be in the (standard) include-path. This distinction also makes it easier to ensure that non-installed headers are not accidentally included in installed ones.

See also considered alternatives.

Implications

Related Decisions

Notes