# Working with Headers

### Working with Headers

C headers are the bridge between Zig and C.

A header file describes functions, structs, constants, enums, macros, and type declarations. Zig reads those declarations through `@cImport`.

For example:

```c
// mathlib.h

#ifndef MATHLIB_H
#define MATHLIB_H

int add(int a, int b);

#endif
```

Zig imports it like this:

```zig
const c = @cImport({
    @cInclude("mathlib.h");
});
```

Then Zig can call:

```zig
const result = c.add(10, 20);
```

The header is not compiled into the final executable. It only describes the API.

#### What a Header Does

A header file answers questions such as:

```text
What functions exist?
What types do they use?
What structs exist?
What constants are defined?
What does the compiler need to know?
```

A header does not usually contain the actual function bodies.

This header:

```c
int add(int a, int b);
```

declares a function.

This source file:

```c
int add(int a, int b) {
    return a + b;
}
```

implements the function.

The header is for compilation.

The source file or library is for linking.

#### Include Guards

Most C headers use include guards.

Example:

```c
#ifndef MATHLIB_H
#define MATHLIB_H

int add(int a, int b);

#endif
```

The pattern prevents multiple inclusion.

Without guards, including the same header more than once may produce duplicate definition errors.

The basic idea:

```text
If MATHLIB_H is not defined:
    define it
    process this header
```

Modern C code may also use:

```c
#pragma once
```

instead of traditional guards.

Both approaches try to solve the same problem.

#### `@cInclude`

Inside Zig, headers are imported with `@cInclude`.

```zig
const c = @cImport({
    @cInclude("stdio.h");
});
```

This is conceptually similar to:

```c
#include <stdio.h>
```

in C.

You can include several headers:

```zig
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("stdlib.h");
    @cInclude("string.h");
});
```

All imported declarations become available through `c`.

```zig
_ = c.puts("hello");
const len = c.strlen("zig");
```

#### Angle Brackets vs Quotes

C supports two common include styles:

```c
#include <stdio.h>
#include "mathlib.h"
```

Angle brackets usually mean:

```text
Search system include paths
```

Quotes usually mean:

```text
Search local project paths first
```

In Zig, `@cInclude` simply takes the header name as a string:

```zig
@cInclude("mathlib.h");
```

The actual search paths come from the build configuration.

#### Include Paths

If Zig cannot find a header, you may see an error like:

```text
'mathlib.h' file not found
```

This means the compiler does not know where the header lives.

Suppose the project layout is:

```text
project/
  build.zig
  include/
    mathlib.h
  src/
    main.zig
```

The build file must add the include directory:

```zig
exe.addIncludePath(b.path("include"));
```

Now this works:

```zig
const c = @cImport({
    @cInclude("mathlib.h");
});
```

Without the include path, Zig cannot locate the header.

#### System Headers

System headers come from the operating system, libc, SDKs, or installed development packages.

Examples:

```zig
@cInclude("stdio.h");
@cInclude("stdlib.h");
@cInclude("string.h");
@cInclude("stdint.h");
```

These headers are usually found automatically if libc and the target environment are configured correctly.

Example:

```zig
const c = @cImport({
    @cInclude("stdio.h");
});

pub fn main() void {
    _ = c.puts("hello");
}
```

#### Local Headers

Project-specific headers usually live in your repository.

Example layout:

```text
project/
  include/
    parser.h
    lexer.h
```

Build file:

```zig
exe.addIncludePath(b.path("include"));
```

Import:

```zig
const c = @cImport({
    @cInclude("parser.h");
    @cInclude("lexer.h");
});
```

The include path tells Zig where those files exist.

#### Organizing Imports

Small projects may import headers directly inside `main.zig`.

```zig
const c = @cImport({
    @cInclude("library.h");
});
```

Larger projects benefit from a dedicated import module.

Example:

```text
src/
  c.zig
  main.zig
  wrapper.zig
```

`src/c.zig`:

```zig
pub const c = @cImport({
    @cInclude("library.h");
    @cInclude("helper.h");
});
```

Other Zig files:

```zig
const c = @import("c.zig").c;
```

This centralizes the raw C boundary.

It also prevents repeated imports across many files.

#### `@cDefine`

Some headers change behavior based on preprocessor macros.

C example:

```c
#define MY_FEATURE 1
#include "library.h"
```

Zig equivalent:

```zig
const c = @cImport({
    @cDefine("MY_FEATURE", "1");
    @cInclude("library.h");
});
```

The macro exists while importing the header.

This is common in libraries that expose optional features or configuration flags.

#### `@cUndef`

You can also undefine a macro:

```zig
const c = @cImport({
    @cDefine("DEBUG", "1");
    @cUndef("DEBUG");
    @cInclude("library.h");
});
```

This is less common, but sometimes useful when a header behaves differently depending on whether a macro exists.

#### Header Order Can Matter

Some headers depend on macros or earlier includes.

Example:

```c
#define FEATURE_X 1
#include "config.h"
#include "library.h"
```

The same logic may matter in Zig:

```zig
const c = @cImport({
    @cDefine("FEATURE_X", "1");
    @cInclude("config.h");
    @cInclude("library.h");
});
```

Headers are not always independent. Some rely on earlier declarations or feature macros.

If a library’s documentation specifies an include order, preserve it.

#### Function Declarations

A header often contains function declarations.

```c
int add(int a, int b);
```

Zig imports the declaration:

```zig
const result = c.add(10, 20);
```

The declaration tells Zig:

```text
Function name
Parameter types
Return type
Calling convention
```

The actual implementation still must be linked separately.

#### Struct Declarations

Headers also declare structs.

```c
typedef struct Point {
    int x;
    int y;
} Point;
```

Imported Zig code can use:

```zig
var p = c.Point{
    .x = 10,
    .y = 20,
};
```

The imported struct uses the C ABI layout.

Opaque structs also appear in headers:

```c
typedef struct Database Database;
```

This declares the type name without exposing fields.

C callers only hold pointers:

```c
Database *database_open(const char *path);
```

This pattern is common in stable library APIs.

#### Constants and Macros

Headers often define constants:

```c
#define MAX_BUFFER 4096
#define MODE_READ 1
```

Imported Zig code may use:

```zig
const max = c.MAX_BUFFER;
const mode = c.MODE_READ;
```

Simple numeric macros usually import cleanly.

Complex macros may not.

#### Function-Like Macros

C macros are preprocessor substitutions, not real functions.

Example:

```c
#define SQUARE(x) ((x) * (x))
```

Some macros import poorly because they depend on textual substitution tricks.

In those cases, write a wrapper function in C:

```c
// wrapper.h

int square_int(int x);
```

```c
// wrapper.c

#define SQUARE(x) ((x) * (x))

int square_int(int x) {
    return SQUARE(x);
}
```

Now Zig imports the wrapper:

```zig
const c = @cImport({
    @cInclude("wrapper.h");
});

const x = c.square_int(9);
```

This is usually easier than trying to expose complicated macros directly.

#### Conditional Compilation in Headers

Headers often use conditional compilation.

Example:

```c
#ifdef _WIN32
void windows_only(void);
#else
void unix_only(void);
#endif
```

Different targets may see different declarations.

That means Zig imports can vary depending on the build target.

Cross-platform projects should expect this.

You may need Zig-side platform checks too:

```zig
const builtin = @import("builtin");

if (builtin.os.tag == .windows) {
    // windows behavior
} else {
    // unix behavior
}
```

#### Header and Library Must Match

A subtle but important rule:

```text
The header and the compiled library must describe the same ABI.
```

Bad combinations cause undefined behavior.

Examples:

```text
Header from version 1
Library from version 2

Header compiled with one macro set
Library compiled with another macro set

Header expects packed layout
Library compiled differently
```

These mismatches may compile successfully and still fail at runtime.

Keep headers and libraries synchronized.

#### C++ Headers

Some headers are actually C++ headers.

Example:

```cpp
class Widget {
public:
    void run();
};
```

Zig cannot directly import arbitrary C++ APIs the same way it imports C APIs.

For C++ libraries, expose a C wrapper layer.

Example:

```cpp
extern "C" int widget_run(Widget *w);
```

Then Zig imports the C wrapper header.

This keeps the ABI stable and predictable.

#### Generating Headers from Zig

If Zig exports functions for C, you should provide a matching C header.

Example Zig:

```zig
export fn mylib_add(a: c_int, b: c_int) c_int {
    return a + b;
}
```

Matching header:

```c
#ifndef MYLIB_H
#define MYLIB_H

int mylib_add(int a, int b);

#endif
```

The header is the public contract for C users.

Keep it simple and stable.

#### Keep Headers Small

A good public header exposes only what users need.

Avoid exposing internal implementation details.

Good:

```c
typedef struct Database Database;

Database *database_open(const char *path);
void database_close(Database *db);
```

Less stable:

```c
typedef struct Database {
    int fd;
    int cache_size;
    int flags;
} Database;
```

The second form freezes the struct layout into the public ABI.

The first form hides implementation details.

#### Common Header Problems

Missing include path:

```text
'library.h' file not found
```

Fix:

```zig
exe.addIncludePath(...);
```

Header/library mismatch:

```text
Compiles successfully
Crashes at runtime
```

Possible cause:

```text
Wrong library version
Wrong macros
Wrong struct layout
```

C++ header imported as C:

```text
Unexpected parse errors
Unknown keywords
```

Fix:

```text
Use a C wrapper layer
```

Macro conflicts:

```text
Duplicate macro definitions
Unexpected macro expansion
```

Fix:

```text
Control macros carefully with @cDefine and @cUndef
```

#### What to Remember

Headers describe APIs.

Headers are for compilation, not linking.

Use `@cInclude` inside `@cImport`.

Use `addIncludePath` so Zig can find local headers.

Use dedicated import modules in larger projects.

Keep headers and compiled libraries synchronized.

Simple constants and declarations import well.

Complex macros and C++ APIs usually need wrappers.

Good headers expose stable contracts and hide implementation details.

