How to Use `fmt::make_format_args` in C++: A Complete Guide to Building Format Argument Stores
fmt::make_format_args builds a compile-time type-safe argument store that holds references to formatting arguments, enabling zero-copy formatting across the {fmt} library.
The {fmt} library provides fmt::make_format_args as the foundational mechanism for capturing formatting arguments before they are rendered. Defined in include/fmt/core.h, this function creates a detail::format_arg_store object that powers every high-level formatting API in the codebase, from fmt::format to fmt::printf.
What Is fmt::make_format_args?
fmt::make_format_args is a variadic template function that constructs a lightweight container holding references to your formatting arguments. According to the source code in include/fmt/core.h (lines 71-78), the function signature is:
template <typename Context = context, typename... T,
int NUM_ARGS = int(sizeof...(T)),
int NUM_NAMED_ARGS= detail::count_named_args<T...>(),
ullong DESC = detail::make_descriptor<Context, T...>()>
constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
-> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
return {{args...}};
}
The template parameters capture compile-time metadata:
Context– The formatting context type, defaulting tofmt::context.NUM_ARGS– Compile-time count of positional arguments derived fromsizeof...(T).NUM_NAMED_ARGS– Count of named arguments created withfmt::arg, calculated viadetail::count_named_args.DESC– A 64-bit compile-time descriptor encoding type information for optimized formatting dispatch.
The function returns a detail::format_arg_store that implicitly converts to fmt::format_args, allowing it to be passed to any API consuming format arguments.
How fmt::make_format_args Works Internally
The implementation stores references (T&... args) rather than copies, which enables zero-allocation formatting paths. This design requires that the original objects outlive the format_args object.
The format_arg_store structure records type information through the DESC template parameter. As implemented in fmtlib/fmt, this descriptor allows the library to select optimal formatting routines at compile time without runtime type introspection.
Internally, fmt::make_format_args serves as the argument forwarding mechanism for higher-level functions. For example, in include/fmt/printf.h (lines 573-574), the printf-style API forwards its arguments through make_format_args:
return fmt::make_format_args<basic_printf_context<Char>>(args...);
Practical Examples
Storing Arguments for Deferred Formatting
Use fmt::make_format_args when you need to capture arguments now but format them later, or when passing arguments through multiple abstraction layers:
#include <fmt/core.h>
#include <string>
int main() {
int value = 42;
std::string name = "answer";
// Build the format argument store.
auto args = fmt::make_format_args(value, name);
// Pass to any function accepting fmt::format_args.
fmt::print("{} is the {}\n", args); // Output: 42 is the answer
}
Using Named Arguments with fmt::make_format_args
When formatting strings with named placeholders, combine fmt::arg with make_format_args:
#include <fmt/core.h>
int main() {
int age = 30;
auto args = fmt::make_format_args(
fmt::arg("name", "Alice"),
fmt::arg("age", age)
);
fmt::print("{name} is {age} years old\n", args);
// Output: Alice is 30 years old
}
Building Custom Formatting Functions
Create wrapper functions that pre-build argument stores for logging or internationalization pipelines:
#include <fmt/core.h>
#include <fmt/format.h>
template <typename... Ts>
void custom_log(fmt::format_string<Ts...> fmt_str, Ts&&... values) {
// Construct argument store once.
auto args = fmt::make_format_args(values...);
// Forward to vprint or custom sinks.
fmt::vprint(fmt_str.str, args);
}
int main() {
custom_log("Error {}: {}\n", 404, "Not Found");
}
Integration with {fmt} Library Components
fmt::make_format_args serves as the bridge between user code and the library's internal formatting engine:
include/fmt/core.h– Contains the primary definition andformat_arg_storeimplementation.include/fmt/printf.h– Usesmake_format_argsto adapt C-style printf calls to the type-safe formatting system.include/fmt/xchar.h– Leverages the same mechanism for wide-character (wchar_t) formatting contexts.include/fmt/format.h– High-level functions likefmt::formatinternally utilizemake_format_argsto process variadic template arguments.
Lifetime and Safety Considerations
Because fmt::make_format_args captures references to its arguments, you must ensure the referenced objects remain valid through the formatting operation. The format_arg_store does not copy string data or arithmetic values—it merely stores pointers and metadata.
Safe usage:
- Arguments passed to
make_format_argsmust outlive the resultingformat_argsobject. - Do not store the returned
format_arg_storebeyond the scope of the referenced variables. - Temporary objects passed directly to
make_format_argsare safe only if formatting occurs immediately in the same expression.
Summary
fmt::make_format_argscreates a zero-copy, type-safe argument container defined ininclude/fmt/core.h.- It returns a
detail::format_arg_storethat converts tofmt::format_argsfor use with any formatting function. - The function captures compile-time metadata (
NUM_ARGS,NUM_NAMED_ARGS,DESC) to optimize formatting performance. - It powers internal APIs including
fmt::printf(ininclude/fmt/printf.h) and wide-character formatting. - Arguments are stored by reference, requiring careful lifetime management.
Frequently Asked Questions
What is the difference between fmt::make_format_args and variadic templates?
fmt::make_format_args converts runtime values into a type-erased yet type-safe container suitable for APIs that cannot expose template parameters in their interface. While variadic templates preserve types at compile time, make_format_args bundles those types into a format_arg_store that can be passed through virtual functions or stored for later processing, as seen in fmt::vprint implementations.
Does fmt::make_format_args copy my strings and objects?
No. The function stores references (T&...) to the original arguments. This zero-copy design enables high-performance formatting but requires that you manage object lifetimes carefully. The source code in include/fmt/core.h explicitly constructs the store as {{args...}}, preserving reference semantics.
Can I use fmt::make_format_args with fmt::arg for named parameters?
Yes. make_format_args accepts fmt::arg objects directly, and the template machinery automatically counts named arguments via detail::count_named_args. This allows you to build dynamic format strings with named placeholders while maintaining compile-time type safety.
When should I use fmt::make_format_args instead of fmt::format directly?
Use fmt::make_format_args when you need to separate argument capture from formatting execution. Common scenarios include implementing custom logging frameworks, queuing format operations for asynchronous processing, or creating adapter layers between your code and {fmt}'s internal APIs. For immediate string formatting, fmt::format remains the simpler choice.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →