# What Is the fmtlib C API Wrapper? Complete Guide to Safe C Formatting

> Explore the fmtlib C API wrapper, a pure C interface for safe formatting in C programs. Learn how this lightweight layer brings type-safe formatting to your C projects.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: tutorial
- Published: 2026-09-10

---

**The fmtlib C API wrapper provides a pure-C interface defined in [`include/fmt/fmt-c.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/fmt-c.h) and implemented in `src/fmt-c.cc` that exposes the {fmt} library's type-safe formatting capabilities to C programs through a lightweight argument translation layer.**

The {fmt} library delivers fast, modern C++ formatting, but the **C API wrapper** extends these benefits to pure C projects without requiring C++ compilation units. By providing a bridge between C data types and the library's C++ core engine, this wrapper enables safe, high-performance string formatting in embedded systems, language bindings, and legacy C codebases.

## Core Architecture of the C API Wrapper

### The fmt_arg Structure and Type Enumeration

The foundation of the **C API wrapper** is the **`fmt_arg` struct**, a discriminated union that safely transports C values into the C++ formatting engine. This struct pairs a **`fmt_type` enum** tag with a union capable of storing integers, floating-point values, booleans, characters, C-strings, and pointers.

The **`fmt_type` enum** enumerates all supported argument categories, allowing the implementation in `src/fmt-c.cc` to dispatch each value to the correct C++ template instantiation.

### Type-Safe Construction Macros

Manual construction of `fmt_arg` instances is error-prone, so the wrapper provides convenience helpers: `fmt_from_int`, `fmt_from_uint`, `fmt_from_double`, `fmt_from_bool`, `fmt_from_char`, `fmt_from_str`, and `fmt_from_ptr`.

The **`FMT_MAKE_ARG(x)` macro** leverages C11 `_Generic` selection to automatically choose the appropriate helper, eliminating manual type specification. For multiple arguments, **`FMT_MAKE_ARGLIST(...)`** expands variadic parameters into a stack-allocated array of `fmt_arg` structures.

## Essential Functions for C Formatting

### Buffer Formatting with fmt_vformat

The **`fmt_vformat`** function writes formatted output to a user-supplied buffer with explicit bounds checking. Its signature is:

```c
int fmt_vformat(char *buf, size_t size, const char *fmt, const fmt_arg *args, size_t num_args);

```

This function returns the number of characters written on success, or a negative error code such as `fmt_error_invalid_arg` on failure. The implementation in `src/fmt-c.cc` converts the `args` array into `fmt::basic_format_arg<fmt::format_context>` objects before forwarding to `fmt::vformat_to_n`.

### Direct Stream Output with fmt_vprint

For immediate output without intermediate buffers, **`fmt_vprint`** writes directly to a `FILE*` stream:

```c
int fmt_vprint(FILE *stream, const char *fmt, const fmt_arg *args, size_t num_args);

```

This function is ideal for logging and console output, utilizing `fmt::vprint` internally while presenting a pure C interface.

### Convenience Macros for Direct Usage

The wrapper exposes **`fmt_format`** and **`fmt_print`** as variadic macros that wrap the `v` variants. These accept standard C variadic arguments and internally expand them using `FMT_MAKE_ARGLIST(...)`, enabling intuitive usage:

```c
char buf[256];
fmt_format(buf, sizeof(buf), "User: %s, ID: %d", "Alice", 42);
fmt_print(stdout, "Hex: %#x, Bool: %s\n", 0xDEAD, true);

```

## Implementation and Safety Details

### Bridging C and C++ Type Systems

In `src/fmt-c.cc`, the wrapper translates each `fmt_arg` into a `fmt::basic_format_arg<fmt::format_context>` that the C++ engine understands. This translation layer maintains zero-overhead abstraction while ensuring C programs cannot trigger C++ exceptions.

The implementation forwards processed calls to **`fmt::vformat_to_n`** for buffer operations or **`fmt::vprint`** for stream output, ensuring C programs benefit from the library's optimized formatting algorithms.

### Argument Limits and Error Handling

To guarantee predictable stack usage in C environments, the wrapper enforces a configurable limit of **16 arguments** (defined by `max_c_format_args`). Exceeding this limit returns `fmt_error_invalid_arg` rather than causing undefined behavior.

Error handling adheres to C conventions: functions return negative integer constants (`fmt_error`, `fmt_error_invalid_arg`) rather than throwing exceptions, making the API suitable for embedded and systems programming contexts.

## Practical Code Examples

### Formatting into a Buffer

```c
#include <stdio.h>
#include "fmt/fmt-c.h"

int main(void) {
    char buf[128];
    int rc = fmt_format(buf, sizeof(buf),
                       "Name: %s, Age: %d, Score: %.2f",
                       "Alice", 30, 95.5);
    if (rc >= 0)
        puts(buf);          // prints: Name: Alice, Age: 30, Score: 95.50
    else
        fprintf(stderr, "Formatting error %d\n", rc);
    return 0;
}

```

### Direct Printing to stdout

```c
#include "fmt/fmt-c.h"

int main(void) {
    /* fmt_print writes directly to the supplied FILE* */
    fmt_print(stdout, "Hex: %#x, Bool: %s\n", 0xDEAD, true);
    /* Output: Hex: 0xdead, Bool: true */
    return 0;
}

```

### Using Automatic Type Selection

```c
#include "fmt/fmt-c.h"

void log_error(const char *msg, int code) {
    fmt_print(stderr, "ERROR: %s (code %d)\n", msg, code);
}

/* The macro automatically creates the fmt_arg array */
int main(void) {
    log_error("File not found", -1);
    return 0;
}

```

## Summary

- The **C API wrapper** consists of the header [`include/fmt/fmt-c.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/fmt-c.h) and implementation `src/fmt-c.cc`, providing pure-C access to {fmt} capabilities.
- The **`fmt_arg` struct** and **`fmt_type` enum** create a type-safe bridge between C values and the C++ formatting engine.
- **`FMT_MAKE_ARG`** leverages C11 `_Generic` for automatic, compile-time type selection without runtime overhead.
- Core functions **`fmt_vformat`** and **`fmt_vprint`** offer buffer and stream-based output with consistent C-style error handling.
- A **16-argument limit** ensures safe stack usage, with negative error codes returned for failure conditions.

## Frequently Asked Questions

### How does the fmtlib C API wrapper differ from standard printf?

Unlike `printf`, the **C API wrapper** enforces type safety through the `fmt_arg` structure and `FMT_MAKE_ARG` macro, eliminating format string vulnerabilities. It supports advanced formatting features from the C++ library—including positional arguments and custom format specifiers—while maintaining a pure C interface compatible with C99 and later standards.

### What is the maximum number of arguments supported by the C API?

The wrapper supports up to **16 arguments** by default, configurable via the `max_c_format_args` constant. This limit ensures predictable stack allocation in resource-constrained environments, returning `fmt_error_invalid_arg` if exceeded rather than causing buffer overflows.

### Can I use the C API wrapper in C++ projects?

While technically possible, C++ projects should use the native `fmt::format` and `fmt::print` APIs directly rather than the **C API wrapper**. The C++ interface provides superior compile-time type checking and performance by avoiding the translation layer implemented in `src/fmt-c.cc`.

### Where are the C API wrapper source files located?

The public interface is defined in **[`include/fmt/fmt-c.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/fmt-c.h)**, while the implementation that converts C arguments to C++ format contexts resides in **`src/fmt-c.cc`**. Both files are maintained in the fmtlib/fmt repository and compiled as part of the standard library build.