From 1df8db868076590b22c8359fe16f0283315b8451 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Fri, 20 Mar 2026 14:28:25 +0100 Subject: [PATCH 1/2] Add FMT_FORMAT_ARGS(fmt, ...) macro to C API to simplify user-defined format wrappers --- include/fmt/fmt-c.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/fmt/fmt-c.h b/include/fmt/fmt-c.h index 20d163a7a179..c82e5d44e9df 100644 --- a/include/fmt/fmt-c.h +++ b/include/fmt/fmt-c.h @@ -181,11 +181,14 @@ typedef enum {} fmt_signed_char; (fmt_arg[]) { FMT_MAP(FMT_MAKE_ARG, ##__VA_ARGS__) } # define FMT_EXPAND(v) v -# define fmt_format(buffer, size, fmt, ...) \ - fmt_vformat((buffer), (size), (fmt), \ - FMT_EXPAND(FMT_VA_SELECT(FMT_MAKE_NULL, FMT_MAKE_ARGLIST, \ - ##__VA_ARGS__)(__VA_ARGS__)), \ - FMT_NARG(, ##__VA_ARGS__)) +# define FMT_FORMAT_ARGS(fmt, ...) \ + (fmt), \ + FMT_EXPAND(FMT_VA_SELECT(FMT_MAKE_NULL, FMT_MAKE_ARGLIST, \ + ##__VA_ARGS__)(__VA_ARGS__)), \ + FMT_NARG(, ##__VA_ARGS__) + +# define fmt_format(buffer, size, fmt, ...) \ + fmt_vformat((buffer), (size), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__)) #endif // __cplusplus From 9bae2b640474e6f8598219c0206b5f1db15edf12 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Fri, 20 Mar 2026 14:30:03 +0100 Subject: [PATCH 2/2] Add fmt_print and fmt_println to C API --- include/fmt/fmt-c.h | 11 ++++++++++ src/fmt-c.cc | 49 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/include/fmt/fmt-c.h b/include/fmt/fmt-c.h index c82e5d44e9df..1a26e8203a1b 100644 --- a/include/fmt/fmt-c.h +++ b/include/fmt/fmt-c.h @@ -10,6 +10,7 @@ #include // bool #include // size_t +#include // FILE #ifdef __cplusplus extern "C" { @@ -46,6 +47,10 @@ enum { fmt_error = -1, fmt_error_invalid_arg = -2 }; int fmt_vformat(char* buffer, size_t size, const char* fmt, const fmt_arg* args, size_t num_args); +int fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args, + size_t num_args); +int fmt_vprintln(FILE* stream, const char* fmt, const fmt_arg* args, + size_t num_args); #ifdef __cplusplus } @@ -190,6 +195,12 @@ typedef enum {} fmt_signed_char; # define fmt_format(buffer, size, fmt, ...) \ fmt_vformat((buffer), (size), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__)) +# define fmt_print(stream, fmt, ...) \ + fmt_vprint((stream), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__)) + +# define fmt_println(stream, fmt, ...) \ + fmt_vprintln((stream), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__)) + #endif // __cplusplus #endif // FMT_C_H_ diff --git a/src/fmt-c.cc b/src/fmt-c.cc index 86de48e972e2..c002482aa5eb 100644 --- a/src/fmt-c.cc +++ b/src/fmt-c.cc @@ -8,13 +8,14 @@ #include "fmt/fmt-c.h" #include +#include -extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt, - const fmt_arg* args, size_t num_args) { - constexpr size_t max_args = 16; - if (num_args > max_args) return fmt_error_invalid_arg; +constexpr size_t max_c_format_args = 16; +static int parse_c_format_args( + fmt::basic_format_arg* format_args, + const fmt_arg* args, size_t num_args) { + if (num_args > max_c_format_args) return fmt_error_invalid_arg; - fmt::basic_format_arg format_args[max_args]; for (size_t i = 0; i < num_args; ++i) { switch (args[i].type) { case fmt_int: format_args[i] = args[i].value.int_value; break; @@ -31,12 +32,44 @@ extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt, default: return fmt_error_invalid_arg; } } + return 0; +} + +template +static int fmt_c_wrapper(const char* fmt, const fmt_arg* args, size_t num_args, + F&& f) { + fmt::basic_format_arg format_args[max_c_format_args]; + int error = parse_c_format_args(format_args, args, num_args); + if (error != 0) return error; + FMT_TRY { - auto result = fmt::vformat_to_n( - buffer, size, fmt, + return std::forward(f)( fmt::format_args(format_args, static_cast(num_args))); - return static_cast(result.size); } FMT_CATCH(...) {} return fmt_error; } + +extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt, + const fmt_arg* args, size_t num_args) { + return fmt_c_wrapper(fmt, args, num_args, [=](fmt::format_args format_args) { + auto result = fmt::vformat_to_n(buffer, size, fmt, format_args); + return static_cast(result.size); + }); +} + +extern "C" int fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args, + size_t num_args) { + return fmt_c_wrapper(fmt, args, num_args, [=](fmt::format_args format_args) { + fmt::vprint(stream, fmt, format_args); + return 0; + }); +} + +extern "C" int fmt_vprintln(FILE* stream, const char* fmt, const fmt_arg* args, + size_t num_args) { + return fmt_c_wrapper(fmt, args, num_args, [=](fmt::format_args format_args) { + fmt::vprintln(stream, fmt, format_args); + return 0; + }); +}