How to Use fmtlib for Character and String Presentation Types

The fmt library selects character presentation via presentation_type::chr when using the 'c' conversion, and string presentation via presentation_type::string when using the 's' conversion, with both types parsed in include/fmt/core.h and handled during final formatting in include/fmt/format.h.

The fmtlib/fmt repository provides a modern C++ formatting library that routes arguments through a strongly-typed format_spec structure. When you use fmtlib for character and string presentation types, the library inspects your format specifier at compile time (or runtime for dynamic strings) to determine whether to treat the argument as a single Unicode code point or as a sequence of characters.

Understanding Presentation Types in fmtlib

Every format operation in fmtlib begins by parsing the format string into a format_spec object. This structure contains a presentation_type enum that dictates how the argument should be rendered.

According to the source code in include/fmt/core.h (around line 654), the presentation_type enum defines distinct values for different formatting behaviors. For text output, two values are critical:

  • presentation_type::chr – Indicates single-character formatting
  • presentation_type::string – Indicates string formatting

The parser determines which type to use by examining the conversion character in your format specifier. Inside include/fmt/core.h, the parse_presentation_type function handles this logic:

case 'c':  // character conversion
    return parse_presentation_type(presentation_type::chr, integral_set);
case 's':  // string conversion
    return parse_presentation_type(presentation_type::string,
                                   cstring_set | string_set);

Once parsing completes, the format_spec stores the selected type (accessible via specs.type()), which the main formatting engine in include/fmt/format.h uses to route output through the appropriate branch in fmt::vformat.

Character Presentation Type (presentation_type::chr)

How Character Formatting Works

When you specify the 'c' conversion, fmtlib treats the argument as a single Unicode code point regardless of whether you pass an integer or a character type. The parse_presentation_type function sets the internal type to presentation_type::chr, and the final output routine in include/fmt/format.h renders the value using the appropriate character encoding (UTF-8, UTF-16, or UTF-32).

This presentation type accepts any integral value that represents a valid code point. The library performs the necessary encoding conversion automatically based on the output context.

Character Formatting Examples

#include <fmt/core.h>

int main() {
    // Format integer 65 as the character 'A'
    fmt::print("Char: {:c}\n", 65);          // Output: Char: A
    
    // Use width and alignment with character presentation
    fmt::print("Padded: {:>5c}\n", 'Z');      // Output: Padded:     Z
    
    // Character presentation with fill character
    fmt::print("Filled: {:*^3c}\n", 'X');     // Output: Filled: *X*
}

The character type respects width, alignment, and fill specifiers, allowing you to pad single characters within a fixed field.

String Presentation Type (presentation_type::string)

How String Formatting Works

The 's' conversion triggers presentation_type::string, which fmtlib applies to char pointers, std::string, std::string_view, and any type implicitly convertible to a C-style string. During parsing in include/fmt/core.h, the parser selects this type when it encounters the 's' conversion or when the argument type matches the string type sets (cstring_set | string_set).

In include/fmt/format.h, the vformat implementation checks specs.type() and routes string arguments through dedicated output routines that respect precision (maximum characters to display) and width (minimum field size).

String Formatting Examples

#include <fmt/core.h>
#include <string>

int main() {
    // Basic string presentation
    std::string name = "fmt";
    fmt::print("Library: {:s}\n", name);     // Output: Library: fmt
    
    // Truncate with precision
    fmt::print("Truncated: {:.3s}\n", "fmtlib"); 
    // Output: Truncated: fmt
    
    // Combine width, alignment, and precision
    fmt::print("|{:<10.5s}|\n", "formatted");
    // Output: |format    |
    
    // C-style string pointer
    const char* msg = "hello";
    fmt::print("Message: {:s}\n", msg);      // Output: Message: hello
}

Format Specifier Reference

Both presentation types support the full fmtlib specification syntax:

Specifier Effect on Character (:c) Effect on String (:s)
Width Minimum field width; pads with fill character Minimum field width; pads if string is shorter
Precision No effect (always single character) Maximum number of characters to output
Align < left, > right, ^ center < left, > right, ^ center
Fill Any character preceding align Any character preceding align

The include/fmt/printf.h header provides C-style printf compatibility and maps %c and %s to these same presentation types internally.

Summary

  • Character presentation (presentation_type::chr) is selected by the 'c' conversion in include/fmt/core.h and renders integral values as single Unicode code points.
  • String presentation (presentation_type::string) is selected by the 's' conversion and handles std::string, std::string_view, and C-style strings through the formatting engine in include/fmt/format.h.
  • Both types support width, alignment, and fill specifications, but only string presentation respects precision for truncation.
  • The parsing logic in parse_presentation_type determines the presentation type at format-string compile time, while fmt::vformat dispatches to the appropriate output routine based on specs.type().

Frequently Asked Questions

What is the difference between {:c} and {:s} in fmtlib?

{:c} treats the argument as a single character code point, accepting integers or character types and rendering exactly one Unicode character, while {:s} treats the argument as a string sequence, accepting pointer types, std::string, or std::string_view and rendering the full sequence (subject to precision limits). The underlying enum values presentation_type::chr and presentation_type::string in include/fmt/core.h distinguish these behaviors.

Can I use precision specifiers with character presentation types?

No, precision specifiers have no effect on the 'c' conversion. Since presentation_type::chr always represents a single code point, the precision field is ignored. If you need to limit output with character types, you must handle the truncation before passing the value to the formatter, or use string presentation if the data is stored as a string.

How does fmtlib handle Unicode when using character presentation?

The fmt library treats character presentation as a Unicode code point and encodes it according to the output context (UTF-8, UTF-16, or UTF-32). When you pass an integer to {:c}, fmtlib validates that it represents a valid code point and performs the necessary encoding conversion in the output routines defined in include/fmt/format.h, ensuring correct multi-byte character generation.

Are these presentation types compatible with C printf-style formatting?

Yes, fmtlib maintains compatibility through include/fmt/printf.h. The %c conversion maps to presentation_type::chr and %s maps to presentation_type::string, routing through the same underlying formatting machinery as the modern {} syntax. However, the modern format specifiers provide additional capabilities like named arguments, compile-time checking, and more precise alignment controls that printf-style formatting cannot express.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →