Skip to content

Conversation

@soumik15630m
Copy link
Contributor

Issues #4663

Summary

Implements a type-safe C11 API for {fmt} using _Generic macros and a fixed-size argument array as discussed in #4663.

Design

  • Type safety: _Generic macros (FMT_MAKE_ARG) automatically map C types to tagged union
  • ABI stability: Explicit struct padding, version tracking, stable binary interface
  • Performance: Fixed-size thread-local array (16 args max), zero allocations for common case
  • Error handling: Thread-local error strings, negative return codes
  • Custom types: Callback-based custom formatters with automatic buffer resizing

Architecture: C type → FmtArg → fixed array → basic_format_arg → vformat

Features

  • All C primitive types (int, float, double, long double, bool, char, pointers)
  • Format specifiers (padding, precision, hex, alignment, positional args)
  • Custom formatters via callbacks
  • Printf-style macros (fmt_printf, fmt_fprintf, fmt_snprintf)
  • Comprehensive error reporting

Testing

40+ test cases covering:

  • All primitive types and format specifiers
  • Buffer overflow safety
  • Custom formatters
  • Error conditions
  • Multi-threading safety (thread-local storage)

Platform Support

  • C11 required
  • MSVC 2019+ with /Zc:preprocessor flag (compile-time check included)
  • GCC/Clang with C11 support
  • Cross-platform DLL export macros

Copy link
Contributor

@vitaut vitaut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

I'll take a closer look and leave comments inline but first let's rename c.h and c.cc to more descriptive fmt-c.h and fmt-c.cc.

include/fmt/c.h Outdated
Comment on lines 15 to 16
# include <cstddef>
# include <cstdint>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed, we should always use C headers here.

Comment on lines +185 to +188
# if defined(_MSC_VER) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL)
# error \
"C API requires MSVC 2019+ with /Zc:preprocessor flag. Add /Zc:preprocessor to your compiler flags."
# endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

becuase i used ' VA_ARGS ' and it was causing problems while compiling with legacy compiler so i tested with zc:preprocessor and it worked ...
it need VA_ARGS becuz of that FMT_MAP uses this to split and apply FMT_MAKE_ARG to each one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this check because the compiler will already give an error if VA_ARGS are not supported. Let's remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure , i'll remove it.
Just to clarify - the legacy preprocessor technically 'supports' VA_ARGS but expands them incorrectly...effectively as a single token , which causes cryptic syntax errors downstream rather than a clear 'unsupported' error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Could you give a godbolt example illustrating the cryptic error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://godbolt.org/z/dcd83Goz3

but if we remove the compiler flags the count comes 1

src/c.cc Outdated
#include <string>
#include <vector>

static const size_t MAX_PACKED_ARGS = FMT_C_MAX_ARGS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed because FMT_C_MAX_ARGS can be used directly.

src/c.cc Outdated
static const size_t MAX_PACKED_ARGS = FMT_C_MAX_ARGS;

extern "C" {
static thread_local std::string g_last_error;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't use mutable globals.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used it - so that fmt_get_c_error can retrieve it ......

but sure , i will change it

just a quick question to remove it should i go with a optional buffer in format functions or just error codes will be enogh

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error codes should be enough.

include/fmt/c.h Outdated
# define FMT_MAP(f, ...) \
FMT_CAT(FMT_MAP_, FMT_NARG(__VA_ARGS__))(f, ##__VA_ARGS__)

# define fmt_snprintf(buf, cap, fmt, ...) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename to fmt_format since it uses a different syntax from printf.

include/fmt/c.h Outdated
Comment on lines 266 to 272
# define fmt_fprintf(f, fmt, ...) \
fmt_c_print( \
f, fmt, \
(FmtArg[]){{FMT_INT}, FMT_MAP(FMT_MAKE_ARG, ##__VA_ARGS__)} + 1, \
FMT_NARG(__VA_ARGS__))

# define fmt_printf(fmt, ...) fmt_fprintf(stdout, fmt, ##__VA_ARGS__)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not include these in the initial implementation and just focus on formatting to a memory buffer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve implemented the requested changes.

For context, these features were already functioning correctly before the update.

Let me know if there’s anything else you’d like me to revise.
Thanks.

@soumik15630m
Copy link
Contributor Author

sure , i will implement the necessary changes

@soumik15630m soumik15630m requested a review from vitaut February 8, 2026 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants