Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Nim
Objective-C
Objective-C++
OCaml
OpenCL C
Pascal
Pony
Python
Racket
Ruby
Rust
Snowball
Scala
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
WASM
Zig
Javascript
GIMPLE
Ygen
c++ source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502-c++ 11.1.0
ARM GCC 10.2.0
ARM GCC 10.3.0
ARM GCC 10.4.0
ARM GCC 10.5.0
ARM GCC 11.1.0
ARM GCC 11.2.0
ARM GCC 11.3.0
ARM GCC 11.4.0
ARM GCC 12.1.0
ARM GCC 12.2.0
ARM GCC 12.3.0
ARM GCC 12.4.0
ARM GCC 13.1.0
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 13.3.0
ARM GCC 13.3.0 (unknown-eabi)
ARM GCC 14.1.0
ARM GCC 14.1.0 (unknown-eabi)
ARM GCC 14.2.0
ARM GCC 14.2.0 (unknown-eabi)
ARM GCC 4.5.4
ARM GCC 4.6.4
ARM GCC 5.4
ARM GCC 6.3.0
ARM GCC 6.4.0
ARM GCC 7.3.0
ARM GCC 7.5.0
ARM GCC 8.2.0
ARM GCC 8.5.0
ARM GCC 9.3.0
ARM GCC 9.4.0
ARM GCC 9.5.0
ARM GCC trunk
ARM gcc 10.2.1 (none)
ARM gcc 10.3.1 (2021.07 none)
ARM gcc 10.3.1 (2021.10 none)
ARM gcc 11.2.1 (none)
ARM gcc 5.4.1 (none)
ARM gcc 7.2.1 (none)
ARM gcc 8.2 (WinCE)
ARM gcc 8.3.1 (none)
ARM gcc 9.2.1 (none)
ARM msvc v19.0 (WINE)
ARM msvc v19.10 (WINE)
ARM msvc v19.14 (WINE)
ARM64 Morello gcc 10.1 Alpha 2
ARM64 gcc 10.2
ARM64 gcc 10.3
ARM64 gcc 10.4
ARM64 gcc 10.5.0
ARM64 gcc 11.1
ARM64 gcc 11.2
ARM64 gcc 11.3
ARM64 gcc 11.4.0
ARM64 gcc 12.1
ARM64 gcc 12.2.0
ARM64 gcc 12.3.0
ARM64 gcc 12.4.0
ARM64 gcc 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 13.3.0
ARM64 gcc 14.1.0
ARM64 gcc 14.2.0
ARM64 gcc 4.9.4
ARM64 gcc 5.4
ARM64 gcc 5.5.0
ARM64 gcc 6.3
ARM64 gcc 6.4
ARM64 gcc 7.3
ARM64 gcc 7.5
ARM64 gcc 8.2
ARM64 gcc 8.5
ARM64 gcc 9.3
ARM64 gcc 9.4
ARM64 gcc 9.5
ARM64 gcc trunk
ARM64 msvc v19.14 (WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 12.4.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF clang 19.1.0
BPF gcc 13.1.0
BPF gcc 13.2.0
BPF gcc 13.3.0
BPF gcc trunk
EDG (experimental reflection)
EDG 6.5
EDG 6.5 (GNU mode gcc 13)
EDG 6.6
EDG 6.6 (GNU mode gcc 13)
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
KVX ACB 5.0.0 (GCC 12.2.1)
KVX ACB 5.2.0 (GCC 13.2.1)
LoongArch64 clang (trunk)
LoongArch64 clang 17.0.1
LoongArch64 clang 18.1.0
LoongArch64 clang 19.1.0
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68k clang (trunk)
MRISC32 gcc (trunk)
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 12.4.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 13.3.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 12.4.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 13.3.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 19.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 19.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 12.4.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI CL430 21.6.1
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.20 VS16.0
arm64 msvc v19.21 VS16.1
arm64 msvc v19.22 VS16.2
arm64 msvc v19.23 VS16.3
arm64 msvc v19.24 VS16.4
arm64 msvc v19.25 VS16.5
arm64 msvc v19.27 VS16.7
arm64 msvc v19.28 VS16.8
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30 VS17.0
arm64 msvc v19.31 VS17.1
arm64 msvc v19.32 VS17.2
arm64 msvc v19.33 VS17.3
arm64 msvc v19.34 VS17.4
arm64 msvc v19.35 VS17.5
arm64 msvc v19.36 VS17.6
arm64 msvc v19.37 VS17.7
arm64 msvc v19.38 VS17.8
arm64 msvc v19.39 VS17.9
arm64 msvc v19.40 VS17.10
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 12.0.0
armv7-a clang 12.0.1
armv7-a clang 13.0.0
armv7-a clang 13.0.1
armv7-a clang 14.0.0
armv7-a clang 15.0.0
armv7-a clang 16.0.0
armv7-a clang 17.0.1
armv7-a clang 18.1.0
armv7-a clang 19.1.0
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 13.0.0
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 19.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clang-cl 18.1.0
ellcc 0.1.33
ellcc 0.1.34
ellcc 2017-07-16
hexagon-clang 16.0.5
llvm-mos atari2600-3e
llvm-mos atari2600-4k
llvm-mos atari2600-common
llvm-mos atari5200-supercart
llvm-mos atari8-cart-megacart
llvm-mos atari8-cart-std
llvm-mos atari8-cart-xegs
llvm-mos atari8-common
llvm-mos atari8-dos
llvm-mos c128
llvm-mos c64
llvm-mos commodore
llvm-mos cpm65
llvm-mos cx16
llvm-mos dodo
llvm-mos eater
llvm-mos mega65
llvm-mos nes
llvm-mos nes-action53
llvm-mos nes-cnrom
llvm-mos nes-gtrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos nes-unrom
llvm-mos nes-unrom-512
llvm-mos osi-c1p
llvm-mos pce
llvm-mos pce-cd
llvm-mos pce-common
llvm-mos pet
llvm-mos rp6502
llvm-mos rpc8e
llvm-mos supervision
llvm-mos vic20
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 12.4.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips clang 19.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 12.4.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 12.4.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 clang 19.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 12.4.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4.0
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mips64el clang 19.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel clang 19.1.0
mipsel gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 12.4.0
mipsel gcc 13.1.0
mipsel gcc 13.2.0
mipsel gcc 13.3.0
mipsel gcc 14.1.0
mipsel gcc 14.2.0
mipsel gcc 4.9.4
mipsel gcc 5.4.0
mipsel gcc 5.5.0
mipsel gcc 9.5.0
nanoMIPS gcc 6.3.0 (mtk)
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 12.4.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64 gcc 11.2.0
power64 gcc 12.1.0
power64 gcc 12.2.0
power64 gcc 12.3.0
power64 gcc 12.4.0
power64 gcc 13.1.0
power64 gcc 13.2.0
power64 gcc 13.3.0
power64 gcc 14.1.0
power64 gcc 14.2.0
power64 gcc trunk
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 12.4.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 12.4.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 14.1.0
s390x gcc 14.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (WINE)
x64 msvc v19.10 (WINE)
x64 msvc v19.14 (WINE)
x64 msvc v19.20 VS16.0
x64 msvc v19.21 VS16.1
x64 msvc v19.22 VS16.2
x64 msvc v19.23 VS16.3
x64 msvc v19.24 VS16.4
x64 msvc v19.25 VS16.5
x64 msvc v19.27 VS16.7
x64 msvc v19.28 VS16.8
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30 VS17.0
x64 msvc v19.31 VS17.1
x64 msvc v19.32 VS17.2
x64 msvc v19.33 VS17.3
x64 msvc v19.34 VS17.4
x64 msvc v19.35 VS17.5
x64 msvc v19.36 VS17.6
x64 msvc v19.37 VS17.7
x64 msvc v19.38 VS17.8
x64 msvc v19.39 VS17.9
x64 msvc v19.40 VS17.10
x64 msvc v19.latest
x86 djgpp 4.9.4
x86 djgpp 5.5.0
x86 djgpp 6.4.0
x86 djgpp 7.2.0
x86 msvc v19.0 (WINE)
x86 msvc v19.10 (WINE)
x86 msvc v19.14 (WINE)
x86 msvc v19.20 VS16.0
x86 msvc v19.21 VS16.1
x86 msvc v19.22 VS16.2
x86 msvc v19.23 VS16.3
x86 msvc v19.24 VS16.4
x86 msvc v19.25 VS16.5
x86 msvc v19.27 VS16.7
x86 msvc v19.28 VS16.8
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30 VS17.0
x86 msvc v19.31 VS17.1
x86 msvc v19.32 VS17.2
x86 msvc v19.33 VS17.3
x86 msvc v19.34 VS17.4
x86 msvc v19.35 VS17.5
x86 msvc v19.36 VS17.6
x86 msvc v19.37 VS17.7
x86 msvc v19.38 VS17.8
x86 msvc v19.39 VS17.9
x86 msvc v19.40 VS17.10
x86 msvc v19.latest
x86 nvc++ 22.11
x86 nvc++ 22.7
x86 nvc++ 22.9
x86 nvc++ 23.1
x86 nvc++ 23.11
x86 nvc++ 23.3
x86 nvc++ 23.5
x86 nvc++ 23.7
x86 nvc++ 23.9
x86 nvc++ 24.1
x86 nvc++ 24.3
x86 nvc++ 24.5
x86 nvc++ 24.7
x86 nvc++ 24.9
x86-64 Zapcc 190308
x86-64 clang (EricWF contracts)
x86-64 clang (amd-staging)
x86-64 clang (assertions trunk)
x86-64 clang (clangir)
x86-64 clang (dascandy contracts)
x86-64 clang (experimental -Wlifetime)
x86-64 clang (experimental P1061)
x86-64 clang (experimental P1144)
x86-64 clang (experimental P1221)
x86-64 clang (experimental P2996)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3367)
x86-64 clang (experimental P3372)
x86-64 clang (experimental metaprogramming - P2632)
x86-64 clang (old concepts branch)
x86-64 clang (p1974)
x86-64 clang (pattern matching - P2688)
x86-64 clang (reflection)
x86-64 clang (resugar)
x86-64 clang (string interpolation - P3412)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (variadic friends - P2893)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.0 (assertions)
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.0 (assertions)
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.0 (assertions)
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.0 (assertions)
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 14.0.0 (assertions)
x86-64 clang 15.0.0
x86-64 clang 15.0.0 (assertions)
x86-64 clang 16.0.0
x86-64 clang 16.0.0 (assertions)
x86-64 clang 17.0.1
x86-64 clang 17.0.1 (assertions)
x86-64 clang 18.1.0
x86-64 clang 18.1.0 (assertions)
x86-64 clang 19.1.0
x86-64 clang 19.1.0 (assertions)
x86-64 clang 2.6.0 (assertions)
x86-64 clang 2.7.0 (assertions)
x86-64 clang 2.8.0 (assertions)
x86-64 clang 2.9.0 (assertions)
x86-64 clang 3.0.0
x86-64 clang 3.0.0 (assertions)
x86-64 clang 3.1
x86-64 clang 3.1 (assertions)
x86-64 clang 3.2
x86-64 clang 3.2 (assertions)
x86-64 clang 3.3
x86-64 clang 3.3 (assertions)
x86-64 clang 3.4 (assertions)
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5 (assertions)
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.6 (assertions)
x86-64 clang 3.7
x86-64 clang 3.7 (assertions)
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8 (assertions)
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.0 (assertions)
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.0 (assertions)
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.0 (assertions)
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.0 (assertions)
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.0 (assertions)
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.0 (assertions)
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.0 (assertions)
x86-64 clang 9.0.1
x86-64 clang rocm-4.5.2
x86-64 clang rocm-5.0.2
x86-64 clang rocm-5.1.3
x86-64 clang rocm-5.2.3
x86-64 clang rocm-5.3.3
x86-64 clang rocm-5.7.0
x86-64 clang rocm-6.0.2
x86-64 clang rocm-6.1.2
x86-64 gcc (contract labels)
x86-64 gcc (contracts natural syntax)
x86-64 gcc (contracts)
x86-64 gcc (coroutines)
x86-64 gcc (modules)
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.4
x86-64 gcc 10.5
x86-64 gcc 11.1
x86-64 gcc 11.2
x86-64 gcc 11.3
x86-64 gcc 11.4
x86-64 gcc 12.1
x86-64 gcc 12.2
x86-64 gcc 12.3
x86-64 gcc 12.4
x86-64 gcc 13.1
x86-64 gcc 13.2
x86-64 gcc 13.3
x86-64 gcc 14.1
x86-64 gcc 14.2
x86-64 gcc 3.4.6
x86-64 gcc 4.0.4
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 5.5
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.4
x86-64 gcc 6.5
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2023.2.1
x86-64 icx 2024.0.0
x86-64 icx 2024.1.0
x86-64 icx 2024.2.0
x86-64 icx 2025.0.0
x86-64 icx 2025.0.0
zig c++ 0.10.0
zig c++ 0.11.0
zig c++ 0.12.0
zig c++ 0.12.1
zig c++ 0.13.0
zig c++ 0.6.0
zig c++ 0.7.0
zig c++ 0.7.1
zig c++ 0.8.0
zig c++ 0.9.0
zig c++ trunk
Options
Source code
// <format> Formatting -*- C++ -*- // Copyright The GNU Toolchain Authors. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // <http://www.gnu.org/licenses/>. /** @file include/format * This is a Standard C++ Library header. */ #ifndef _GLIBCXX_FORMAT #define _GLIBCXX_FORMAT 1 #include <bits/requires_hosted.h> // for std::string #define __glibcxx_want_format #define __glibcxx_want_format_ranges #define __glibcxx_want_format_uchar #include <bits/version.h> #ifdef __cpp_lib_format // C++ >= 20 && HOSTED #include <array> #include <charconv> #include <concepts> #include <limits> #include <locale> #include <optional> #include <span> #include <string_view> #include <string> #include <variant> // monostate (TODO: move to bits/utility.h?) #include <bits/ranges_base.h> // input_range, range_reference_t #include <bits/ranges_util.h> // subrange #include <bits/ranges_algobase.h> // ranges::copy #include <bits/stl_iterator.h> // back_insert_iterator #include <bits/stl_pair.h> // __is_pair #include <bits/unicode.h> // __is_scalar_value, _Utf_view, etc. #include <bits/utility.h> // tuple_size_v #include <ext/numeric_traits.h> // __int_traits #if !__has_builtin(__builtin_toupper) # include <cctype> #endif namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION // [format.context], class template basic_format_context template<typename _Out, typename _CharT> class basic_format_context; // [format.fmt.string], class template basic_format_string template<typename _CharT, typename... _Args> struct basic_format_string; /// @cond undocumented namespace __format { // Type-erased character sink. template<typename _CharT> class _Sink; // Output iterator that writes to a type-erase character sink. template<typename _CharT> class _Sink_iter; template<typename _CharT> using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>; template<typename _CharT> struct _Runtime_format_string { [[__gnu__::__always_inline__]] _Runtime_format_string(basic_string_view<_CharT> __s) noexcept : _M_str(__s) { } _Runtime_format_string(const _Runtime_format_string&) = delete; void operator=(const _Runtime_format_string&) = delete; private: basic_string_view<_CharT> _M_str; template<typename, typename...> friend struct std::basic_format_string; }; } // namespace __format /// @endcond using format_context = __format::__format_context<char>; #ifdef _GLIBCXX_USE_WCHAR_T using wformat_context = __format::__format_context<wchar_t>; #endif // [format.args], class template basic_format_args template<typename _Context> class basic_format_args; using format_args = basic_format_args<format_context>; #ifdef _GLIBCXX_USE_WCHAR_T using wformat_args = basic_format_args<wformat_context>; #endif // [format.arguments], arguments // [format.arg], class template basic_format_arg template<typename _Context> class basic_format_arg; /** A compile-time checked format string for the specified argument types. * * @since C++23 but available as an extension in C++20. */ template<typename _CharT, typename... _Args> struct basic_format_string { template<typename _Tp> requires convertible_to<const _Tp&, basic_string_view<_CharT>> consteval basic_format_string(const _Tp& __s); [[__gnu__::__always_inline__]] basic_format_string(__format::_Runtime_format_string<_CharT> __s) noexcept : _M_str(__s._M_str) { } [[__gnu__::__always_inline__]] constexpr basic_string_view<_CharT> get() const noexcept { return _M_str; } private: basic_string_view<_CharT> _M_str; }; template<typename... _Args> using format_string = basic_format_string<char, type_identity_t<_Args>...>; #ifdef _GLIBCXX_USE_WCHAR_T template<typename... _Args> using wformat_string = basic_format_string<wchar_t, type_identity_t<_Args>...>; #endif #if __cpp_lib_format >= 202311L // >= C++26 [[__gnu__::__always_inline__]] inline __format::_Runtime_format_string<char> runtime_format(string_view __fmt) noexcept { return __fmt; } #ifdef _GLIBCXX_USE_WCHAR_T [[__gnu__::__always_inline__]] inline __format::_Runtime_format_string<wchar_t> runtime_format(wstring_view __fmt) noexcept { return __fmt; } #endif #endif // C++26 // [format.formatter], formatter /// The primary template of std::formatter is disabled. template<typename _Tp, typename _CharT = char> struct formatter { formatter() = delete; // No std::formatter specialization for this type. formatter(const formatter&) = delete; formatter& operator=(const formatter&) = delete; }; // [format.error], class format_error class format_error : public runtime_error { public: explicit format_error(const string& __what) : runtime_error(__what) { } explicit format_error(const char* __what) : runtime_error(__what) { } }; /// @cond undocumented [[noreturn]] inline void __throw_format_error(const char* __what) { _GLIBCXX_THROW_OR_ABORT(format_error(__what)); } namespace __format { // XXX use named functions for each constexpr error? [[noreturn]] inline void __unmatched_left_brace_in_format_string() { __throw_format_error("format error: unmatched '{' in format string"); } [[noreturn]] inline void __unmatched_right_brace_in_format_string() { __throw_format_error("format error: unmatched '}' in format string"); } [[noreturn]] inline void __conflicting_indexing_in_format_string() { __throw_format_error("format error: conflicting indexing style in format string"); } [[noreturn]] inline void __invalid_arg_id_in_format_string() { __throw_format_error("format error: invalid arg-id in format string"); } [[noreturn]] inline void __failed_to_parse_format_spec() { __throw_format_error("format error: failed to parse format-spec"); } template<typename _CharT> class _Scanner; } // namespace __format /// @endcond // [format.parse.ctx], class template basic_format_parse_context template<typename _CharT> class basic_format_parse_context; using format_parse_context = basic_format_parse_context<char>; #ifdef _GLIBCXX_USE_WCHAR_T using wformat_parse_context = basic_format_parse_context<wchar_t>; #endif template<typename _CharT> class basic_format_parse_context { public: using char_type = _CharT; using const_iterator = typename basic_string_view<_CharT>::const_iterator; using iterator = const_iterator; constexpr explicit basic_format_parse_context(basic_string_view<_CharT> __fmt) noexcept : _M_begin(__fmt.begin()), _M_end(__fmt.end()) { } basic_format_parse_context(const basic_format_parse_context&) = delete; void operator=(const basic_format_parse_context&) = delete; constexpr const_iterator begin() const noexcept { return _M_begin; } constexpr const_iterator end() const noexcept { return _M_end; } constexpr void advance_to(const_iterator __it) noexcept { _M_begin = __it; } constexpr size_t next_arg_id() { if (_M_indexing == _Manual) __format::__conflicting_indexing_in_format_string(); _M_indexing = _Auto; // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3825. Missing compile-time argument id check in next_arg_id if (std::is_constant_evaluated()) if (_M_next_arg_id == _M_num_args) __format::__invalid_arg_id_in_format_string(); return _M_next_arg_id++; } constexpr void check_arg_id(size_t __id) { if (_M_indexing == _Auto) __format::__conflicting_indexing_in_format_string(); _M_indexing = _Manual; if (std::is_constant_evaluated()) if (__id >= _M_num_args) __format::__invalid_arg_id_in_format_string(); } #if __cpp_lib_format >= 202305L template<typename... _Ts> constexpr void check_dynamic_spec(size_t __id) noexcept; constexpr void check_dynamic_spec_integral(size_t __id) noexcept { check_dynamic_spec<int, unsigned, long long, unsigned long long>(__id); } constexpr void check_dynamic_spec_string(size_t __id) noexcept { check_dynamic_spec<const char_type*, basic_string_view<_CharT>>(__id); } private: // Check the Mandates: condition for check_dynamic_spec<Ts...>(n) template<typename... _Ts> static consteval bool __check_dynamic_spec_types() { if constexpr (sizeof...(_Ts)) { int __counts[] = { (is_same_v<bool, _Ts> + ...), (is_same_v<_CharT, _Ts> + ...), (is_same_v<int, _Ts> + ...), (is_same_v<unsigned, _Ts> + ...), (is_same_v<long long, _Ts> + ...), (is_same_v<unsigned long long, _Ts> + ...), (is_same_v<float, _Ts> + ...), (is_same_v<double, _Ts> + ...), (is_same_v<long double, _Ts> + ...), (is_same_v<const _CharT*, _Ts> + ...), (is_same_v<basic_string_view<_CharT>, _Ts> + ...), (is_same_v<const void*, _Ts> + ...) }; int __sum = 0; for (int __c : __counts) { __sum += __c; if (__c > 1) __invalid_dynamic_spec("non-unique template argument type"); } if (__sum != sizeof...(_Ts)) __invalid_dynamic_spec("disallowed template argument type"); } return true; } // This must not be constexpr. static void __invalid_dynamic_spec(const char*); friend __format::_Scanner<_CharT>; #endif // This constructor should only be used by the implementation. constexpr explicit basic_format_parse_context(basic_string_view<_CharT> __fmt, size_t __num_args) noexcept : _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args) { } private: iterator _M_begin; iterator _M_end; enum _Indexing { _Unknown, _Manual, _Auto }; _Indexing _M_indexing = _Unknown; size_t _M_next_arg_id = 0; size_t _M_num_args = 0; }; /// @cond undocumented template<typename _Tp, template<typename...> class _Class> static constexpr bool __is_specialization_of = false; template<template<typename...> class _Class, typename... _Args> static constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true; namespace __format { // pre: first != last template<typename _CharT> constexpr pair<unsigned short, const _CharT*> __parse_integer(const _CharT* __first, const _CharT* __last) { if (__first == __last) __builtin_unreachable(); if constexpr (is_same_v<_CharT, char>) { const auto __start = __first; unsigned short __val = 0; // N.B. std::from_chars is not constexpr in C++20. if (__detail::__from_chars_alnum<true>(__first, __last, __val, 10) && __first != __start) [[likely]] return {__val, __first}; } else { constexpr int __n = 32; char __buf[__n]{}; for (int __i = 0; __i < __n && (__first + __i) != __last; ++__i) __buf[__i] = __first[__i]; auto [__v, __ptr] = __format::__parse_integer(__buf, __buf + __n); if (__ptr) [[likely]] return {__v, __first + (__ptr - __buf)}; } return {0, nullptr}; } template<typename _CharT> constexpr pair<unsigned short, const _CharT*> __parse_arg_id(const _CharT* __first, const _CharT* __last) { if (__first == __last) __builtin_unreachable(); if (*__first == '0') return {0, __first + 1}; // No leading zeros allowed, so '0...' == 0 if ('1' <= *__first && *__first <= '9') { const unsigned short __id = *__first - '0'; const auto __next = __first + 1; // Optimize for most likely case of single digit arg-id. if (__next == __last || !('0' <= *__next && *__next <= '9')) return {__id, __next}; else return __format::__parse_integer(__first, __last); } return {0, nullptr}; } enum _Pres_type { _Pres_none = 0, // Default type (not valid for integer presentation types). // Presentation types for integral types (including bool and charT). _Pres_d = 1, _Pres_b, _Pres_B, _Pres_o, _Pres_x, _Pres_X, _Pres_c, // Presentation types for floating-point types. _Pres_a = 1, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F, _Pres_g, _Pres_G, _Pres_p = 0, _Pres_P, // For pointers. _Pres_s = 0, // For strings and bool. _Pres_esc = 0xf, // For strings and charT. }; enum _Align { _Align_default, _Align_left, _Align_right, _Align_centre, }; enum _Sign { _Sign_default, _Sign_plus, _Sign_minus, // XXX does this need to be distinct from _Sign_default? _Sign_space, }; enum _WidthPrec { _WP_none, // No width/prec specified. _WP_value, // Fixed width/prec specified. _WP_from_arg // Use a formatting argument for width/prec. }; template<typename _Context> size_t __int_from_arg(const basic_format_arg<_Context>& __arg); constexpr bool __is_digit(char __c) { return std::__detail::__from_chars_alnum_to_val(__c) < 10; } constexpr bool __is_xdigit(char __c) { return std::__detail::__from_chars_alnum_to_val(__c) < 16; } template<typename _CharT> struct _Spec { _Align _M_align : 2; _Sign _M_sign : 2; unsigned _M_alt : 1; unsigned _M_localized : 1; unsigned _M_zero_fill : 1; _WidthPrec _M_width_kind : 2; _WidthPrec _M_prec_kind : 2; _Pres_type _M_type : 4; unsigned _M_reserved : 1; unsigned _M_reserved2 : 16; unsigned short _M_width; unsigned short _M_prec; char32_t _M_fill = ' '; using iterator = typename basic_string_view<_CharT>::iterator; static constexpr _Align _S_align(_CharT __c) noexcept { switch (__c) { case '<': return _Align_left; case '>': return _Align_right; case '^': return _Align_centre; default: return _Align_default; } } // pre: __first != __last constexpr iterator _M_parse_fill_and_align(iterator __first, iterator __last) noexcept { if (*__first != '{') { using namespace __unicode; if constexpr (__literal_encoding_is_unicode<_CharT>()) { // Accept any UCS scalar value as fill character. _Utf32_view<ranges::subrange<iterator>> __uv({__first, __last}); if (!__uv.empty()) { auto __beg = __uv.begin(); char32_t __c = *__beg++; if (__is_scalar_value(__c)) if (auto __next = __beg.base(); __next != __last) if (_Align __align = _S_align(*__next)) { _M_fill = __c; _M_align = __align; return ++__next; } } } else if (__last - __first >= 2) if (_Align __align = _S_align(__first[1])) { _M_fill = *__first; _M_align = __align; return __first + 2; } if (_Align __align = _S_align(__first[0])) { _M_fill = ' '; _M_align = __align; return __first + 1; } } return __first; } static constexpr _Sign _S_sign(_CharT __c) noexcept { switch (__c) { case '+': return _Sign_plus; case '-': return _Sign_minus; case ' ': return _Sign_space; default: return _Sign_default; } } // pre: __first != __last constexpr iterator _M_parse_sign(iterator __first, iterator) noexcept { if (_Sign __sign = _S_sign(*__first)) { _M_sign = __sign; return __first + 1; } return __first; } // pre: *__first is valid constexpr iterator _M_parse_alternate_form(iterator __first, iterator) noexcept { if (*__first == '#') { _M_alt = true; ++__first; } return __first; } // pre: __first != __last constexpr iterator _M_parse_zero_fill(iterator __first, iterator /* __last */) noexcept { if (*__first == '0') { _M_zero_fill = true; ++__first; } return __first; } // pre: __first != __last static constexpr iterator _S_parse_width_or_precision(iterator __first, iterator __last, unsigned short& __val, bool& __arg_id, basic_format_parse_context<_CharT>& __pc) { if (__format::__is_digit(*__first)) { auto [__v, __ptr] = __format::__parse_integer(__first, __last); if (!__ptr) __throw_format_error("format error: invalid width or precision " "in format-spec"); __first = __ptr; __val = __v; } else if (*__first == '{') { __arg_id = true; ++__first; if (__first == __last) __format::__unmatched_left_brace_in_format_string(); if (*__first == '}') __val = __pc.next_arg_id(); else { auto [__v, __ptr] = __format::__parse_arg_id(__first, __last); if (__ptr == nullptr || __ptr == __last || *__ptr != '}') __format::__invalid_arg_id_in_format_string(); __first = __ptr; __pc.check_arg_id(__v); __val = __v; } #if __cpp_lib_format >= 202305L __pc.check_dynamic_spec_integral(__val); #endif ++__first; // past the '}' } return __first; } // pre: __first != __last constexpr iterator _M_parse_width(iterator __first, iterator __last, basic_format_parse_context<_CharT>& __pc) { bool __arg_id = false; if (*__first == '0') __throw_format_error("format error: width must be non-zero in " "format string"); auto __next = _S_parse_width_or_precision(__first, __last, _M_width, __arg_id, __pc); if (__next != __first) _M_width_kind = __arg_id ? _WP_from_arg : _WP_value; return __next; } // pre: __first != __last constexpr iterator _M_parse_precision(iterator __first, iterator __last, basic_format_parse_context<_CharT>& __pc) { if (__first[0] != '.') return __first; iterator __next = ++__first; bool __arg_id = false; if (__next != __last) __next = _S_parse_width_or_precision(__first, __last, _M_prec, __arg_id, __pc); if (__next == __first) __throw_format_error("format error: missing precision after '.' in " "format string"); _M_prec_kind = __arg_id ? _WP_from_arg : _WP_value; return __next; } // pre: __first != __last constexpr iterator _M_parse_locale(iterator __first, iterator /* __last */) noexcept { if (*__first == 'L') { _M_localized = true; ++__first; } return __first; } template<typename _Context> size_t _M_get_width(_Context& __ctx) const { size_t __width = 0; if (_M_width_kind == _WP_value) __width = _M_width; else if (_M_width_kind == _WP_from_arg) __width = __format::__int_from_arg(__ctx.arg(_M_width)); return __width; } template<typename _Context> size_t _M_get_precision(_Context& __ctx) const { size_t __prec = -1; if (_M_prec_kind == _WP_value) __prec = _M_prec; else if (_M_prec_kind == _WP_from_arg) __prec = __format::__int_from_arg(__ctx.arg(_M_prec)); return __prec; } }; template<typename _Int> inline char* __put_sign(_Int __i, _Sign __sign, char* __dest) noexcept { if (__i < 0) *__dest = '-'; else if (__sign == _Sign_plus) *__dest = '+'; else if (__sign == _Sign_space) *__dest = ' '; else ++__dest; return __dest; } // Write STR to OUT (and do so efficiently if OUT is a _Sink_iter). template<typename _Out, typename _CharT> requires output_iterator<_Out, const _CharT&> inline _Out __write(_Out __out, basic_string_view<_CharT> __str) { if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) { if (__str.size()) __out = __str; } else for (_CharT __c : __str) *__out++ = __c; return __out; } // Write STR to OUT with NFILL copies of FILL_CHAR specified by ALIGN. // pre: __align != _Align_default template<typename _Out, typename _CharT> constexpr _Out __write_padded(_Out __out, basic_string_view<_CharT> __str, _Align __align, size_t __nfill, char32_t __fill_char) { const size_t __buflen = 0x20; _CharT __padding_chars[__buflen]; __padding_chars[0] = _CharT(); basic_string_view<_CharT> __padding{__padding_chars, __buflen}; auto __pad = [&__padding] (size_t __n, _Out& __o) { if (__n == 0) return; while (__n > __padding.size()) { __o = __format::__write(std::move(__o), __padding); __n -= __padding.size(); } if (__n != 0) __o = __format::__write(std::move(__o), __padding.substr(0, __n)); }; size_t __l, __r, __max; if (__align == _Align_centre) { __l = __nfill / 2; __r = __l + (__nfill & 1); __max = __r; } else if (__align == _Align_right) { __l = __nfill; __r = 0; __max = __l; } else { __l = 0; __r = __nfill; __max = __r; } using namespace __unicode; if constexpr (__literal_encoding_is_unicode<_CharT>()) if (!__is_single_code_unit<_CharT>(__fill_char)) [[unlikely]] { // Encode fill char as multiple code units of type _CharT. const char32_t __arr[1]{ __fill_char }; _Utf_view<_CharT, const char32_t(&)[1]> __v(__arr); basic_string<_CharT> __padstr(__v.begin(), __v.end()); __padding = __padstr; while (__l-- > 0) __out = __format::__write(std::move(__out), __padding); __out = __format::__write(std::move(__out), __str); while (__r-- > 0) __out = __format::__write(std::move(__out), __padding); return __out; } if (__max < __buflen) __padding.remove_suffix(__buflen - __max); else __max = __buflen; char_traits<_CharT>::assign(__padding_chars, __max, __fill_char); __pad(__l, __out); __out = __format::__write(std::move(__out), __str); __pad(__r, __out); return __out; } // Write STR to OUT, with alignment and padding as determined by SPEC. // pre: __spec._M_align != _Align_default || __align != _Align_default template<typename _CharT, typename _Out> _Out __write_padded_as_spec(basic_string_view<type_identity_t<_CharT>> __str, size_t __estimated_width, basic_format_context<_Out, _CharT>& __fc, const _Spec<_CharT>& __spec, _Align __align = _Align_left) { size_t __width = __spec._M_get_width(__fc); if (__width <= __estimated_width) return __format::__write(__fc.out(), __str); const size_t __nfill = __width - __estimated_width; if (__spec._M_align) __align = __spec._M_align; return __format::__write_padded(__fc.out(), __str, __align, __nfill, __spec._M_fill); } // A lightweight optional<locale>. struct _Optional_locale { [[__gnu__::__always_inline__]] _Optional_locale() : _M_dummy(), _M_hasval(false) { } _Optional_locale(const locale& __loc) noexcept : _M_loc(__loc), _M_hasval(true) { } _Optional_locale(const _Optional_locale& __l) noexcept : _M_dummy(), _M_hasval(__l._M_hasval) { if (_M_hasval) std::construct_at(&_M_loc, __l._M_loc); } _Optional_locale& operator=(const _Optional_locale& __l) noexcept { if (_M_hasval) { if (__l._M_hasval) _M_loc = __l._M_loc; else { _M_loc.~locale(); _M_hasval = false; } } else if (__l._M_hasval) { std::construct_at(&_M_loc, __l._M_loc); _M_hasval = true; } return *this; } ~_Optional_locale() { if (_M_hasval) _M_loc.~locale(); } _Optional_locale& operator=(locale&& __loc) noexcept { if (_M_hasval) _M_loc = std::move(__loc); else { std::construct_at(&_M_loc, std::move(__loc)); _M_hasval = true; } return *this; } const locale& value() noexcept { if (!_M_hasval) { std::construct_at(&_M_loc); _M_hasval = true; } return _M_loc; } bool has_value() const noexcept { return _M_hasval; } union { char _M_dummy = '\0'; std::locale _M_loc; }; bool _M_hasval = false; }; #ifdef _GLIBCXX_USE_WCHAR_T template<typename _CharT> concept __char = same_as<_CharT, char> || same_as<_CharT, wchar_t>; #else template<typename _CharT> concept __char = same_as<_CharT, char>; #endif template<__char _CharT> struct __formatter_str { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { auto __first = __pc.begin(); const auto __last = __pc.end(); _Spec<_CharT> __spec{}; auto __finalize = [this, &__spec] { _M_spec = __spec; }; auto __finished = [&] { if (__first == __last || *__first == '}') { __finalize(); return true; } return false; }; if (__finished()) return __first; __first = __spec._M_parse_fill_and_align(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_width(__first, __last, __pc); if (__finished()) return __first; __first = __spec._M_parse_precision(__first, __last, __pc); if (__finished()) return __first; if (*__first == 's') ++__first; #if __cpp_lib_format_ranges else if (*__first == '?') { __spec._M_type = _Pres_esc; ++__first; } #endif if (__finished()) return __first; __format::__failed_to_parse_format_spec(); } template<typename _Out> _Out format(basic_string_view<_CharT> __s, basic_format_context<_Out, _CharT>& __fc) const { if (_M_spec._M_type == _Pres_esc) { // TODO: C++23 escaped string presentation } if (_M_spec._M_width_kind == _WP_none && _M_spec._M_prec_kind == _WP_none) return __format::__write(__fc.out(), __s); size_t __estimated_width; if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) { if (_M_spec._M_prec_kind != _WP_none) { size_t __prec = _M_spec._M_get_precision(__fc); __estimated_width = __unicode::__truncate(__s, __prec); } else __estimated_width = __unicode::__field_width(__s); } else { __s = __s.substr(0, _M_spec._M_get_precision(__fc)); __estimated_width = __s.size(); } return __format::__write_padded_as_spec(__s, __estimated_width, __fc, _M_spec); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_spec._M_type = _Pres_esc; } #endif private: _Spec<_CharT> _M_spec{}; }; template<__char _CharT> struct __formatter_int { // If no presentation type is specified, meaning of "none" depends // whether we are formatting an integer or a char or a bool. static constexpr _Pres_type _AsInteger = _Pres_d; static constexpr _Pres_type _AsBool = _Pres_s; static constexpr _Pres_type _AsChar = _Pres_c; constexpr typename basic_format_parse_context<_CharT>::iterator _M_do_parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type) { _Spec<_CharT> __spec{}; __spec._M_type = __type; const auto __last = __pc.end(); auto __first = __pc.begin(); auto __finalize = [this, &__spec] { _M_spec = __spec; }; auto __finished = [&] { if (__first == __last || *__first == '}') { __finalize(); return true; } return false; }; if (__finished()) return __first; __first = __spec._M_parse_fill_and_align(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_sign(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_alternate_form(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_zero_fill(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_width(__first, __last, __pc); if (__finished()) return __first; __first = __spec._M_parse_locale(__first, __last); if (__finished()) return __first; switch (*__first) { case 'b': __spec._M_type = _Pres_b; ++__first; break; case 'B': __spec._M_type = _Pres_B; ++__first; break; case 'c': // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3586. format should not print bool with 'c' if (__type != _AsBool) { __spec._M_type = _Pres_c; ++__first; } break; case 'd': __spec._M_type = _Pres_d; ++__first; break; case 'o': __spec._M_type = _Pres_o; ++__first; break; case 'x': __spec._M_type = _Pres_x; ++__first; break; case 'X': __spec._M_type = _Pres_X; ++__first; break; case 's': if (__type == _AsBool) { __spec._M_type = _Pres_s; // same value (and meaning) as "none" ++__first; } break; #if __cpp_lib_format_ranges case '?': if (__type == _AsChar) { __spec._M_type = _Pres_esc; ++__first; } #endif break; } if (__finished()) return __first; __format::__failed_to_parse_format_spec(); } template<typename _Tp> constexpr typename basic_format_parse_context<_CharT>::iterator _M_parse(basic_format_parse_context<_CharT>& __pc) { if constexpr (is_same_v<_Tp, bool>) { auto __end = _M_do_parse(__pc, _AsBool); if (_M_spec._M_type == _Pres_s) if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill) __throw_format_error("format error: format-spec contains " "invalid formatting options for " "'bool'"); return __end; } else if constexpr (__char<_Tp>) { auto __end = _M_do_parse(__pc, _AsChar); if (_M_spec._M_type == _Pres_c || _M_spec._M_type == _Pres_esc) if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill /* XXX should be invalid? || _M_spec._M_localized */) __throw_format_error("format error: format-spec contains " "invalid formatting options for " "'charT'"); return __end; } else return _M_do_parse(__pc, _AsInteger); } template<typename _Int, typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Int __i, basic_format_context<_Out, _CharT>& __fc) const { if (_M_spec._M_type == _Pres_c) return _M_format_character(_S_to_character(__i), __fc); char __buf[sizeof(_Int) * __CHAR_BIT__ + 3]; to_chars_result __res{}; string_view __base_prefix; make_unsigned_t<_Int> __u; if (__i < 0) __u = -static_cast<make_unsigned_t<_Int>>(__i); else __u = __i; char* __start = __buf + 3; char* const __end = __buf + sizeof(__buf); char* const __start_digits = __start; switch (_M_spec._M_type) { case _Pres_b: case _Pres_B: __base_prefix = _M_spec._M_type == _Pres_b ? "0b" : "0B"; __res = to_chars(__start, __end, __u, 2); break; #if 0 case _Pres_c: return _M_format_character(_S_to_character(__i), __fc); #endif case _Pres_none: // Should not reach here with _Pres_none for bool or charT, so: [[fallthrough]]; case _Pres_d: __res = to_chars(__start, __end, __u, 10); break; case _Pres_o: if (__i != 0) __base_prefix = "0"; __res = to_chars(__start, __end, __u, 8); break; case _Pres_x: case _Pres_X: __base_prefix = _M_spec._M_type == _Pres_x ? "0x" : "0X"; __res = to_chars(__start, __end, __u, 16); if (_M_spec._M_type == _Pres_X) for (auto __p = __start; __p != __res.ptr; ++__p) #if __has_builtin(__builtin_toupper) *__p = __builtin_toupper(*__p); #else *__p = std::toupper(*__p); #endif break; default: __builtin_unreachable(); } if (_M_spec._M_alt && __base_prefix.size()) { __start -= __base_prefix.size(); __builtin_memcpy(__start, __base_prefix.data(), __base_prefix.size()); } __start = __format::__put_sign(__i, _M_spec._M_sign, __start - 1); return _M_format_int(string_view(__start, __res.ptr - __start), __start_digits - __start, __fc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(bool __i, basic_format_context<_Out, _CharT>& __fc) const { if (_M_spec._M_type == _Pres_c) return _M_format_character(static_cast<unsigned char>(__i), __fc); if (_M_spec._M_type != _Pres_s) return format(static_cast<unsigned char>(__i), __fc); basic_string<_CharT> __s; size_t __est_width; if (_M_spec._M_localized) [[unlikely]] { auto& __np = std::use_facet<numpunct<_CharT>>(__fc.locale()); __s = __i ? __np.truename() : __np.falsename(); __est_width = __s.size(); // TODO Unicode-aware estimate } else { if constexpr (is_same_v<char, _CharT>) __s = __i ? "true" : "false"; else __s = __i ? L"true" : L"false"; __est_width = __s.size(); } return __format::__write_padded_as_spec(__s, __est_width, __fc, _M_spec); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator _M_format_character(_CharT __c, basic_format_context<_Out, _CharT>& __fc) const { return __format::__write_padded_as_spec({&__c, 1u}, 1, __fc, _M_spec); } template<typename _Int> static _CharT _S_to_character(_Int __i) { using _Traits = __gnu_cxx::__int_traits<_CharT>; if constexpr (is_signed_v<_Int> == is_signed_v<_CharT>) { if (_Traits::__min <= __i && __i <= _Traits::__max) return static_cast<_CharT>(__i); } else if constexpr (is_signed_v<_Int>) { if (__i >= 0 && make_unsigned_t<_Int>(__i) <= _Traits::__max) return static_cast<_CharT>(__i); } else if (__i <= make_unsigned_t<_CharT>(_Traits::__max)) return static_cast<_CharT>(__i); __throw_format_error("format error: integer not representable as " "character"); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator _M_format_int(string_view __narrow_str, size_t __prefix_len, basic_format_context<_Out, _CharT>& __fc) const { size_t __width = _M_spec._M_get_width(__fc); basic_string_view<_CharT> __str; if constexpr (is_same_v<char, _CharT>) __str = __narrow_str; #ifdef _GLIBCXX_USE_WCHAR_T else { size_t __n = __narrow_str.size(); auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT)); std::__to_wstring_numeric(__narrow_str.data(), __n, __p); __str = {__p, __n}; } #endif if (_M_spec._M_localized) { const auto& __l = __fc.locale(); if (__l.name() != "C") { auto& __np = use_facet<numpunct<_CharT>>(__l); string __grp = __np.grouping(); if (!__grp.empty()) { size_t __n = __str.size() - __prefix_len; auto __p = (_CharT*)__builtin_alloca(2 * __n * sizeof(_CharT) + __prefix_len); auto __s = __str.data(); char_traits<_CharT>::copy(__p, __s, __prefix_len); __s += __prefix_len; auto __end = std::__add_grouping(__p + __prefix_len, __np.thousands_sep(), __grp.data(), __grp.size(), __s, __s + __n); __str = {__p, size_t(__end - __p)}; } } } if (__width <= __str.size()) return __format::__write(__fc.out(), __str); char32_t __fill_char = _M_spec._M_fill; _Align __align = _M_spec._M_align; size_t __nfill = __width - __str.size(); auto __out = __fc.out(); if (__align == _Align_default) { __align = _Align_right; if (_M_spec._M_zero_fill) { __fill_char = _CharT('0'); // Write sign and base prefix before zero filling. if (__prefix_len != 0) { __out = __format::__write(std::move(__out), __str.substr(0, __prefix_len)); __str.remove_prefix(__prefix_len); } } else __fill_char = _CharT(' '); } return __format::__write_padded(std::move(__out), __str, __align, __nfill, __fill_char); } #if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__ template<typename _Tp> using make_unsigned_t = typename __conditional_t<(sizeof(_Tp) <= sizeof(long long)), std::make_unsigned<_Tp>, type_identity<unsigned __int128>>::type; // std::to_chars is not overloaded for int128 in strict mode. template<typename _Int> static to_chars_result to_chars(char* __first, char* __last, _Int __value, int __base) { return std::__to_chars_i<_Int>(__first, __last, __value, __base); } #endif _Spec<_CharT> _M_spec{}; }; // Decide how 128-bit floating-point types should be formatted (or not). // When supported, the typedef __format::__float128_t is the type that // format arguments should be converted to for storage in basic_format_arg. // Define the macro _GLIBCXX_FORMAT_F128 to say they're supported. // _GLIBCXX_FORMAT_F128=1 means __float128, _Float128 etc. will be formatted // by converting them to long double (or __ieee128 for powerpc64le). // _GLIBCXX_FORMAT_F128=2 means basic_format_arg needs to enable explicit // support for _Float128, rather than formatting it as another type. #undef _GLIBCXX_FORMAT_F128 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // Format 128-bit floating-point types using __ieee128. using __float128_t = __ieee128; # define _GLIBCXX_FORMAT_F128 1 #ifdef __LONG_DOUBLE_IEEE128__ // These overloads exist in the library, but are not declared. // Make them available as std::__format::to_chars. to_chars_result to_chars(char*, char*, __ibm128) noexcept __asm("_ZSt8to_charsPcS_e"); to_chars_result to_chars(char*, char*, __ibm128, chars_format) noexcept __asm("_ZSt8to_charsPcS_eSt12chars_format"); to_chars_result to_chars(char*, char*, __ibm128, chars_format, int) noexcept __asm("_ZSt8to_charsPcS_eSt12chars_formati"); #elif __cplusplus == 202002L to_chars_result to_chars(char*, char*, __ieee128) noexcept __asm("_ZSt8to_charsPcS_u9__ieee128"); to_chars_result to_chars(char*, char*, __ieee128, chars_format) noexcept __asm("_ZSt8to_charsPcS_u9__ieee128St12chars_format"); to_chars_result to_chars(char*, char*, __ieee128, chars_format, int) noexcept __asm("_ZSt8to_charsPcS_u9__ieee128St12chars_formati"); #endif #elif defined _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 // Format 128-bit floating-point types using long double. using __float128_t = long double; # define _GLIBCXX_FORMAT_F128 1 #elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH) // Format 128-bit floating-point types using _Float128. using __float128_t = _Float128; # define _GLIBCXX_FORMAT_F128 2 # if __cplusplus == 202002L // These overloads exist in the library, but are not declared for C++20. // Make them available as std::__format::to_chars. to_chars_result to_chars(char*, char*, _Float128) noexcept # if _GLIBCXX_INLINE_VERSION __asm("_ZNSt3__88to_charsEPcS0_DF128_"); # else __asm("_ZSt8to_charsPcS_DF128_"); # endif to_chars_result to_chars(char*, char*, _Float128, chars_format) noexcept # if _GLIBCXX_INLINE_VERSION __asm("_ZNSt3__88to_charsEPcS0_DF128_NS_12chars_formatE"); # else __asm("_ZSt8to_charsPcS_DF128_St12chars_format"); # endif to_chars_result to_chars(char*, char*, _Float128, chars_format, int) noexcept # if _GLIBCXX_INLINE_VERSION __asm("_ZNSt3__88to_charsEPcS0_DF128_NS_12chars_formatEi"); # else __asm("_ZSt8to_charsPcS_DF128_St12chars_formati"); # endif # endif #endif using std::to_chars; // We can format a floating-point type iff it is usable with to_chars. template<typename _Tp> concept __formattable_float = requires (_Tp __t, char* __p) { __format::to_chars(__p, __p, __t, chars_format::scientific, 6); }; template<__char _CharT> struct __formatter_fp { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { _Spec<_CharT> __spec{}; const auto __last = __pc.end(); auto __first = __pc.begin(); auto __finalize = [this, &__spec] { _M_spec = __spec; }; auto __finished = [&] { if (__first == __last || *__first == '}') { __finalize(); return true; } return false; }; if (__finished()) return __first; __first = __spec._M_parse_fill_and_align(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_sign(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_alternate_form(__first, __last); if (__finished()) return __first; __first = __spec._M_parse_zero_fill(__first, __last); if (__finished()) return __first; if (__first[0] != '.') { __first = __spec._M_parse_width(__first, __last, __pc); if (__finished()) return __first; } __first = __spec._M_parse_precision(__first, __last, __pc); if (__finished()) return __first; __first = __spec._M_parse_locale(__first, __last); if (__finished()) return __first; switch (*__first) { case 'a': __spec._M_type = _Pres_a; ++__first; break; case 'A': __spec._M_type = _Pres_A; ++__first; break; case 'e': __spec._M_type = _Pres_e; ++__first; break; case 'E': __spec._M_type = _Pres_E; ++__first; break; case 'f': __spec._M_type = _Pres_f; ++__first; break; case 'F': __spec._M_type = _Pres_F; ++__first; break; case 'g': __spec._M_type = _Pres_g; ++__first; break; case 'G': __spec._M_type = _Pres_G; ++__first; break; } if (__finished()) return __first; __format::__failed_to_parse_format_spec(); } template<typename _Fp, typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Fp __v, basic_format_context<_Out, _CharT>& __fc) const { std::string __dynbuf; char __buf[128]; to_chars_result __res{}; size_t __prec = 6; bool __use_prec = _M_spec._M_prec_kind != _WP_none; if (__use_prec) __prec = _M_spec._M_get_precision(__fc); char* __start = __buf + 1; // reserve space for sign char* __end = __buf + sizeof(__buf); chars_format __fmt{}; bool __upper = false; bool __trailing_zeros = false; char __expc = 'e'; switch (_M_spec._M_type) { case _Pres_A: __upper = true; __expc = 'P'; [[fallthrough]]; case _Pres_a: if (_M_spec._M_type != _Pres_A) __expc = 'p'; __fmt = chars_format::hex; break; case _Pres_E: __upper = true; __expc = 'E'; [[fallthrough]]; case _Pres_e: __use_prec = true; __fmt = chars_format::scientific; break; case _Pres_F: __upper = true; [[fallthrough]]; case _Pres_f: __use_prec = true; __fmt = chars_format::fixed; break; case _Pres_G: __upper = true; __expc = 'E'; [[fallthrough]]; case _Pres_g: __trailing_zeros = true; __use_prec = true; __fmt = chars_format::general; break; case _Pres_none: if (__use_prec) __fmt = chars_format::general; break; default: __builtin_unreachable(); } // Write value into buffer using std::to_chars. auto __to_chars = [&](char* __b, char* __e) { if (__use_prec) return __format::to_chars(__b, __e, __v, __fmt, __prec); else if (__fmt != chars_format{}) return __format::to_chars(__b, __e, __v, __fmt); else return __format::to_chars(__b, __e, __v); }; // First try using stack buffer. __res = __to_chars(__start, __end); if (__builtin_expect(__res.ec == errc::value_too_large, 0)) { // If the buffer is too small it's probably because of a large // precision, or a very large value in fixed format. size_t __guess = 8 + __prec; if (__fmt == chars_format::fixed) // +ddd.prec { if constexpr (is_same_v<_Fp, float> || is_same_v<_Fp, double> || is_same_v<_Fp, long double>) { // The number of digits to the left of the decimal point // is floor(log10(max(abs(__v),1)))+1 int __exp{}; if constexpr (is_same_v<_Fp, float>) __builtin_frexpf(__v, &__exp); else if constexpr (is_same_v<_Fp, double>) __builtin_frexp(__v, &__exp); else if constexpr (is_same_v<_Fp, long double>) __builtin_frexpl(__v, &__exp); if (__exp > 0) __guess += 1U + __exp * 4004U / 13301U; // log10(2) approx. } else __guess += numeric_limits<_Fp>::max_exponent10; } if (__guess <= sizeof(__buf)) [[unlikely]] __guess = sizeof(__buf) * 2; __dynbuf.reserve(__guess); do { auto __overwrite = [&__to_chars, &__res] (char* __p, size_t __n) { __res = __to_chars(__p + 1, __p + __n - 1); return __res.ec == errc{} ? __res.ptr - __p : 0; }; __dynbuf.__resize_and_overwrite(__dynbuf.capacity() * 2, __overwrite); __start = __dynbuf.data() + 1; // reserve space for sign __end = __dynbuf.data() + __dynbuf.size(); } while (__builtin_expect(__res.ec == errc::value_too_large, 0)); } // Use uppercase for 'A', 'E', and 'G' formats. if (__upper) { for (char* __p = __start; __p != __res.ptr; ++__p) *__p = std::toupper(*__p); } bool __have_sign = true; // Add sign for non-negative values. if (!__builtin_signbit(__v)) { if (_M_spec._M_sign == _Sign_plus) *--__start = '+'; else if (_M_spec._M_sign == _Sign_space) *--__start = ' '; else __have_sign = false; } string_view __narrow_str(__start, __res.ptr - __start); // Use alternate form. Ensure decimal point is always present, // and add trailing zeros (up to precision) for g and G forms. if (_M_spec._M_alt && __builtin_isfinite(__v)) { string_view __s = __narrow_str; size_t __sigfigs; // Number of significant figures. size_t __z = 0; // Number of trailing zeros to add. size_t __p; // Position of the exponent character (if any). size_t __d = __s.find('.'); // Position of decimal point. if (__d != __s.npos) // Found decimal point. { __p = __s.find(__expc, __d + 1); if (__p == __s.npos) __p = __s.size(); // If presentation type is g or G we might need to add zeros. if (__trailing_zeros) { // Find number of digits after first significant figure. if (__s[__have_sign] != '0') // A string like "D.D" or "-D.DDD" __sigfigs = __p - __have_sign - 1; else // A string like "0.D" or "-0.0DD". // Safe to assume there is a non-zero digit, because // otherwise there would be no decimal point. __sigfigs = __p - __s.find_first_not_of('0', __d + 1); } } else // No decimal point, we need to insert one. { __p = __s.find(__expc); // Find the exponent, if present. if (__p == __s.npos) __p = __s.size(); __d = __p; // Position where '.' should be inserted. __sigfigs = __d - __have_sign; } if (__trailing_zeros && __prec != 0) { // For g and G presentation types std::to_chars produces // no more than prec significant figures. Insert this many // zeros so the result has exactly prec significant figures. __z = __prec - __sigfigs; } if (size_t __extras = int(__d == __p) + __z) // How many to add. { if (__dynbuf.empty() && __extras <= size_t(__end - __res.ptr)) { // The stack buffer is large enough for the result. // Move exponent to make space for extra chars. __builtin_memmove(__start + __p + __extras, __start + __p, __s.size() - __p); if (__d == __p) __start[__p++] = '.'; __builtin_memset(__start + __p, '0', __z); __narrow_str = {__s.data(), __s.size() + __extras}; } else // Need to switch to the dynamic buffer. { __dynbuf.reserve(__s.size() + __extras); if (__dynbuf.empty()) { __dynbuf = __s.substr(0, __p); if (__d == __p) __dynbuf += '.'; if (__z) __dynbuf.append(__z, '0'); __dynbuf.append(__s.substr(__p)); } else { __dynbuf.insert(__p, __extras, '0'); if (__d == __p) __dynbuf[__p] = '.'; } __narrow_str = __dynbuf; } } } basic_string<_CharT> __wstr; basic_string_view<_CharT> __str; if constexpr (is_same_v<_CharT, char>) __str = __narrow_str; #ifdef _GLIBCXX_USE_WCHAR_T else { __wstr = std::__to_wstring_numeric(__narrow_str); __str = __wstr; } #endif if (_M_spec._M_localized && __builtin_isfinite(__v)) { __wstr = _M_localize(__str, __expc, __fc.locale()); if (!__wstr.empty()) __str = __wstr; } size_t __width = _M_spec._M_get_width(__fc); if (__width <= __str.size()) return __format::__write(__fc.out(), __str); char32_t __fill_char = _M_spec._M_fill; _Align __align = _M_spec._M_align; size_t __nfill = __width - __str.size(); auto __out = __fc.out(); if (__align == _Align_default) { __align = _Align_right; if (_M_spec._M_zero_fill && __builtin_isfinite(__v)) { __fill_char = _CharT('0'); // Write sign before zero filling. if (!__format::__is_xdigit(__narrow_str[0])) { *__out++ = __str[0]; __str.remove_prefix(1); } } else __fill_char = _CharT(' '); } return __format::__write_padded(std::move(__out), __str, __align, __nfill, __fill_char); } // Locale-specific format. basic_string<_CharT> _M_localize(basic_string_view<_CharT> __str, char __expc, const locale& __loc) const { basic_string<_CharT> __lstr; if (__loc == locale::classic()) return __lstr; // Nothing to do. const auto& __np = use_facet<numpunct<_CharT>>(__loc); const _CharT __point = __np.decimal_point(); const string __grp = __np.grouping(); _CharT __dot, __exp; if constexpr (is_same_v<_CharT, char>) { __dot = '.'; __exp = __expc; } else { __dot = L'.'; switch (__expc) { case 'e': __exp = L'e'; break; case 'E': __exp = L'E'; break; case 'p': __exp = L'p'; break; case 'P': __exp = L'P'; break; default: __builtin_unreachable(); } } if (__grp.empty() && __point == __dot) return __lstr; // Locale uses '.' and no grouping. size_t __d = __str.find(__dot); size_t __e = min(__d, __str.find(__exp)); if (__e == __str.npos) __e = __str.size(); const size_t __r = __str.size() - __e; auto __overwrite = [&](_CharT* __p, size_t) { auto __end = std::__add_grouping(__p, __np.thousands_sep(), __grp.data(), __grp.size(), __str.data(), __str.data() + __e); if (__r) { if (__d != __str.npos) { *__end = __point; ++__end; ++__e; } if (__r > 1) __end += __str.copy(__end, __str.npos, __e); } return (__end - __p); }; __lstr.__resize_and_overwrite(__e * 2 + __r, __overwrite); return __lstr; } _Spec<_CharT> _M_spec{}; }; } // namespace __format /// @endcond /// Format a character. template<__format::__char _CharT> struct formatter<_CharT, _CharT> { formatter() = default; constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.template _M_parse<_CharT>(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_CharT __u, basic_format_context<_Out, _CharT>& __fc) const { if (_M_f._M_spec._M_type == __format::_Pres_none || _M_f._M_spec._M_type == __format::_Pres_c) return _M_f._M_format_character(__u, __fc); else if (_M_f._M_spec._M_type == __format::_Pres_esc) { // TODO return __fc.out(); } else return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f._M_spec._M_type = __format::_Pres_esc; } #endif private: __format::__formatter_int<_CharT> _M_f; }; #ifdef _GLIBCXX_USE_WCHAR_T /// Format a char value for wide character output. template<> struct formatter<char, wchar_t> { formatter() = default; constexpr typename basic_format_parse_context<wchar_t>::iterator parse(basic_format_parse_context<wchar_t>& __pc) { return _M_f._M_parse<char>(__pc); } template<typename _Out> typename basic_format_context<_Out, wchar_t>::iterator format(char __u, basic_format_context<_Out, wchar_t>& __fc) const { if (_M_f._M_spec._M_type == __format::_Pres_none || _M_f._M_spec._M_type == __format::_Pres_c) return _M_f._M_format_character(__u, __fc); else if (_M_f._M_spec._M_type == __format::_Pres_esc) { // TODO return __fc.out(); } else return _M_f.format(static_cast<unsigned char>(__u), __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f._M_spec._M_type = __format::_Pres_esc; } #endif private: __format::__formatter_int<wchar_t> _M_f; }; #endif // USE_WCHAR_T /** Format a string. * @{ */ template<__format::__char _CharT> struct formatter<_CharT*, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> [[__gnu__::__nonnull__]] typename basic_format_context<_Out, _CharT>::iterator format(_CharT* __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__u, __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } #endif private: __format::__formatter_str<_CharT> _M_f; }; template<__format::__char _CharT> struct formatter<const _CharT*, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> [[__gnu__::__nonnull__]] typename basic_format_context<_Out, _CharT>::iterator format(const _CharT* __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__u, __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } #endif private: __format::__formatter_str<_CharT> _M_f; }; template<__format::__char _CharT, size_t _Nm> struct formatter<_CharT[_Nm], _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const _CharT (&__u)[_Nm], basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format({__u, _Nm}, __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } #endif private: __format::__formatter_str<_CharT> _M_f; }; template<typename _Traits, typename _Alloc> struct formatter<basic_string<char, _Traits, _Alloc>, char> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<char>::iterator parse(basic_format_parse_context<char>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, char>::iterator format(const basic_string<char, _Traits, _Alloc>& __u, basic_format_context<_Out, char>& __fc) const { return _M_f.format(__u, __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } #endif private: __format::__formatter_str<char> _M_f; }; #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Traits, typename _Alloc> struct formatter<basic_string<wchar_t, _Traits, _Alloc>, wchar_t> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<wchar_t>::iterator parse(basic_format_parse_context<wchar_t>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, wchar_t>::iterator format(const basic_string<wchar_t, _Traits, _Alloc>& __u, basic_format_context<_Out, wchar_t>& __fc) const { return _M_f.format(__u, __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } #endif private: __format::__formatter_str<wchar_t> _M_f; }; #endif // USE_WCHAR_T template<typename _Traits> struct formatter<basic_string_view<char, _Traits>, char> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<char>::iterator parse(basic_format_parse_context<char>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, char>::iterator format(basic_string_view<char, _Traits> __u, basic_format_context<_Out, char>& __fc) const { return _M_f.format(__u, __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } #endif private: __format::__formatter_str<char> _M_f; }; #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Traits> struct formatter<basic_string_view<wchar_t, _Traits>, wchar_t> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<wchar_t>::iterator parse(basic_format_parse_context<wchar_t>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, wchar_t>::iterator format(basic_string_view<wchar_t, _Traits> __u, basic_format_context<_Out, wchar_t>& __fc) const { return _M_f.format(__u, __fc); } #if __cpp_lib_format_ranges constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } #endif private: __format::__formatter_str<wchar_t> _M_f; }; #endif // USE_WCHAR_T /// @} /// Format an integer. template<integral _Tp, __format::__char _CharT> requires (!__is_one_of<_Tp, char, wchar_t, char16_t, char32_t>::value) struct formatter<_Tp, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.template _M_parse<_Tp>(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__u, __fc); } private: __format::__formatter_int<_CharT> _M_f; }; #if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__ template<typename _Tp, __format::__char _CharT> requires (__is_one_of<_Tp, __int128, unsigned __int128>::value) struct formatter<_Tp, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.template _M_parse<_Tp>(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__u, __fc); } private: __format::__formatter_int<_CharT> _M_f; }; #endif #if defined __glibcxx_to_chars /// Format a floating-point value. template<__format::__formattable_float _Tp, __format::__char _CharT> struct formatter<_Tp, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__ // Reuse __formatter_fp<C>::format<double, Out> for long double. template<__format::__char _CharT> struct formatter<long double, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(long double __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format((double)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #endif #ifdef __STDCPP_FLOAT16_T__ // Reuse __formatter_fp<C>::format<float, Out> for _Float16. template<__format::__char _CharT> struct formatter<_Float16, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Float16 __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format((float)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #endif #if defined(__FLT32_DIG__) // Reuse __formatter_fp<C>::format<float, Out> for _Float32. template<__format::__char _CharT> struct formatter<_Float32, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Float32 __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format((float)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #endif #if defined(__FLT64_DIG__) // Reuse __formatter_fp<C>::format<double, Out> for _Float64. template<__format::__char _CharT> struct formatter<_Float64, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Float64 __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format((double)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #endif #if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 == 1 // Reuse __formatter_fp<C>::format<__float128_t, Out> for _Float128. template<__format::__char _CharT> struct formatter<_Float128, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format((__format::__float128_t)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #endif #ifdef __STDCPP_BFLOAT16_T__ // Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t. template<__format::__char _CharT> struct formatter<__gnu_cxx::__bfloat16_t, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(__gnu_cxx::__bfloat16_t __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format((float)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #endif #endif // __cpp_lib_to_chars /** Format a pointer. * @{ */ template<__format::__char _CharT> struct formatter<const void*, _CharT> { formatter() = default; constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { __format::_Spec<_CharT> __spec{}; const auto __last = __pc.end(); auto __first = __pc.begin(); auto __finalize = [this, &__spec] { _M_spec = __spec; }; auto __finished = [&] { if (__first == __last || *__first == '}') { __finalize(); return true; } return false; }; if (__finished()) return __first; __first = __spec._M_parse_fill_and_align(__first, __last); if (__finished()) return __first; // _GLIBCXX_RESOLVE_LIB_DEFECTS // P2510R3 Formatting pointers #if __glibcxx_format >= 202304L __first = __spec._M_parse_zero_fill(__first, __last); if (__finished()) return __first; #endif __first = __spec._M_parse_width(__first, __last, __pc); if (__first != __last) { if (*__first == 'p') ++__first; #if __glibcxx_format >= 202304L else if (*__first == 'P') { __spec._M_type = __format::_Pres_P; ++__first; } #endif } if (__finished()) return __first; __format::__failed_to_parse_format_spec(); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const { auto __u = reinterpret_cast<__UINTPTR_TYPE__>(__v); char __buf[2 + sizeof(__v) * 2]; auto [__ptr, __ec] = std::to_chars(__buf + 2, std::end(__buf), __u, 16); int __n = __ptr - __buf; __buf[0] = '0'; __buf[1] = 'x'; #if __glibcxx_format >= 202304L if (_M_spec._M_type == __format::_Pres_P) { __buf[1] = 'X'; for (auto __p = __buf + 2; __p != __ptr; ++__p) #if __has_builtin(__builtin_toupper) *__p = __builtin_toupper(*__p); #else *__p = std::toupper(*__p); #endif } #endif basic_string_view<_CharT> __str; if constexpr (is_same_v<_CharT, char>) __str = string_view(__buf, __n); #ifdef _GLIBCXX_USE_WCHAR_T else { auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT)); std::__to_wstring_numeric(__buf, __n, __p); __str = wstring_view(__p, __n); } #endif #if __glibcxx_format >= 202304L if (_M_spec._M_zero_fill) { size_t __width = _M_spec._M_get_width(__fc); if (__width <= __str.size()) return __format::__write(__fc.out(), __str); auto __out = __fc.out(); // Write "0x" or "0X" prefix before zero-filling. __out = __format::__write(std::move(__out), __str.substr(0, 2)); __str.remove_prefix(2); size_t __nfill = __width - __n; return __format::__write_padded(std::move(__out), __str, __format::_Align_right, __nfill, _CharT('0')); } #endif return __format::__write_padded_as_spec(__str, __n, __fc, _M_spec, __format::_Align_right); } private: __format::_Spec<_CharT> _M_spec{}; }; template<__format::__char _CharT> struct formatter<void*, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(void* __v, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__v, __fc); } private: formatter<const void*, _CharT> _M_f; }; template<__format::__char _CharT> struct formatter<nullptr_t, _CharT> { formatter() = default; [[__gnu__::__always_inline__]] constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(nullptr_t, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(nullptr, __fc); } private: formatter<const void*, _CharT> _M_f; }; /// @} #if defined _GLIBCXX_USE_WCHAR_T && __cpp_lib_format_ranges // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3944. Formatters converting sequences of char to sequences of wchar_t namespace __format { struct __disabled; } // std::formatter<__disabled, C> uses the primary template, which is disabled. template<> struct formatter<char*, wchar_t> : private formatter<__format::__disabled, wchar_t> { }; template<> struct formatter<const char*, wchar_t> : private formatter<__format::__disabled, wchar_t> { }; template<size_t _Nm> struct formatter<char[_Nm], wchar_t> : private formatter<__format::__disabled, wchar_t> { }; template<class _Traits, class _Allocator> struct formatter<basic_string<char, _Traits, _Allocator>, wchar_t> : private formatter<__format::__disabled, wchar_t> { }; template<class _Traits> struct formatter<basic_string_view<char, _Traits>, wchar_t> : private formatter<__format::__disabled, wchar_t> { }; #endif /// @cond undocumented namespace __format { template<typename _Tp, typename _Context, typename _Formatter = typename _Context::template formatter_type<remove_const_t<_Tp>>, typename _ParseContext = basic_format_parse_context<typename _Context::char_type>> concept __parsable_with = semiregular<_Formatter> && requires (_Formatter __f, _ParseContext __pc) { { __f.parse(__pc) } -> same_as<typename _ParseContext::iterator>; }; template<typename _Tp, typename _Context, typename _Formatter = typename _Context::template formatter_type<remove_const_t<_Tp>>, typename _ParseContext = basic_format_parse_context<typename _Context::char_type>> concept __formattable_with = semiregular<_Formatter> && requires (const _Formatter __cf, _Tp&& __t, _Context __fc) { { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>; }; // An unspecified output iterator type used in the `formattable` concept. template<typename _CharT> using _Iter_for = back_insert_iterator<basic_string<_CharT>>; template<typename _Tp, typename _CharT, typename _Context = basic_format_context<_Iter_for<_CharT>, _CharT>> concept __formattable_impl = __parsable_with<_Tp, _Context> && __formattable_with<_Tp, _Context>; } // namespace __format /// @endcond #if __cplusplus > 202002L // [format.formattable], concept formattable template<typename _Tp, typename _CharT> concept formattable = __format::__formattable_impl<remove_reference_t<_Tp>, _CharT>; #endif #if __cpp_lib_format_ranges /// @cond undocumented namespace __format { template<typename _Rg, typename _CharT> concept __const_formattable_range = ranges::input_range<const _Rg> && formattable<ranges::range_reference_t<const _Rg>, _CharT>; template<typename _Rg, typename _CharT> using __maybe_const_range = conditional_t<__const_formattable_range<_Rg, _CharT>, const _Rg, _Rg>; } // namespace __format /// @endcond #endif // format_ranges /// An iterator after the last character written, and the number of /// characters that would have been written. template<typename _Out> struct format_to_n_result { _Out out; iter_difference_t<_Out> size; }; _GLIBCXX_BEGIN_NAMESPACE_CONTAINER template<typename, typename> class vector; _GLIBCXX_END_NAMESPACE_CONTAINER /// @cond undocumented namespace __format { template<typename _CharT> class _Sink_iter { _Sink<_CharT>* _M_sink = nullptr; public: using iterator_category = output_iterator_tag; using value_type = void; using difference_type = ptrdiff_t; using pointer = void; using reference = void; _Sink_iter() = default; _Sink_iter(const _Sink_iter&) = default; _Sink_iter& operator=(const _Sink_iter&) = default; [[__gnu__::__always_inline__]] explicit constexpr _Sink_iter(_Sink<_CharT>& __sink) : _M_sink(std::addressof(__sink)) { } [[__gnu__::__always_inline__]] constexpr _Sink_iter& operator=(_CharT __c) { _M_sink->_M_write(__c); return *this; } [[__gnu__::__always_inline__]] constexpr _Sink_iter& operator=(basic_string_view<_CharT> __s) { _M_sink->_M_write(__s); return *this; } [[__gnu__::__always_inline__]] constexpr _Sink_iter& operator*() { return *this; } [[__gnu__::__always_inline__]] constexpr _Sink_iter& operator++() { return *this; } [[__gnu__::__always_inline__]] constexpr _Sink_iter operator++(int) { return *this; } auto _M_reserve(size_t __n) const { return _M_sink->_M_reserve(__n); } }; // Abstract base class for type-erased character sinks. // All formatting and output is done via this type's iterator, // to reduce the number of different template instantiations. template<typename _CharT> class _Sink { friend class _Sink_iter<_CharT>; span<_CharT> _M_span; typename span<_CharT>::iterator _M_next; // Called when the span is full, to make more space available. // Precondition: _M_next != _M_span.begin() // Postcondition: _M_next != _M_span.end() // TODO: remove the precondition? could make overflow handle it. virtual void _M_overflow() = 0; protected: // Precondition: __span.size() != 0 [[__gnu__::__always_inline__]] explicit constexpr _Sink(span<_CharT> __span) noexcept : _M_span(__span), _M_next(__span.begin()) { } // The portion of the span that has been written to. [[__gnu__::__always_inline__]] span<_CharT> _M_used() const noexcept { return _M_span.first(_M_next - _M_span.begin()); } // The portion of the span that has not been written to. [[__gnu__::__always_inline__]] constexpr span<_CharT> _M_unused() const noexcept { return _M_span.subspan(_M_next - _M_span.begin()); } // Use the start of the span as the next write position. [[__gnu__::__always_inline__]] constexpr void _M_rewind() noexcept { _M_next = _M_span.begin(); } // Replace the current output range. void _M_reset(span<_CharT> __s, size_t __pos = 0) noexcept { _M_span = __s; _M_next = __s.begin() + __pos; } // Called by the iterator for *it++ = c constexpr void _M_write(_CharT __c) { *_M_next++ = __c; if (_M_next - _M_span.begin() == std::ssize(_M_span)) [[unlikely]] _M_overflow(); } constexpr void _M_write(basic_string_view<_CharT> __s) { span __to = _M_unused(); while (__to.size() <= __s.size()) { __s.copy(__to.data(), __to.size()); _M_next += __to.size(); __s.remove_prefix(__to.size()); _M_overflow(); __to = _M_unused(); } if (__s.size()) { __s.copy(__to.data(), __s.size()); _M_next += __s.size(); } } // A successful _Reservation can be used to directly write // up to N characters to the sink to avoid unwanted buffering. struct _Reservation { // True if the reservation was successful, false otherwise. explicit operator bool() const noexcept { return _M_sink; } // A pointer to write directly to the sink. _CharT* get() const noexcept { return _M_sink->_M_next.operator->(); } // Add n to the _M_next iterator for the sink. void _M_bump(size_t __n) { _M_sink->_M_bump(__n); } _Sink* _M_sink; }; // Attempt to reserve space to write n characters to the sink. // If anything is written to the reservation then there must be a call // to _M_bump(N2) before any call to another member function of *this, // where N2 is the number of characters written. virtual _Reservation _M_reserve(size_t __n) { if (__n <= _M_unused().size()) return { this }; if (__n <= _M_span.size()) // Cannot meet the request. { _M_overflow(); // Make more space available. if (__n <= _M_unused().size()) return { this }; } return { nullptr }; } // Update the next output position after writing directly to the sink. // pre: no calls to _M_write or _M_overflow since _M_reserve. virtual void _M_bump(size_t __n) { _M_next += __n; } public: _Sink(const _Sink&) = delete; _Sink& operator=(const _Sink&) = delete; [[__gnu__::__always_inline__]] constexpr _Sink_iter<_CharT> out() noexcept { return _Sink_iter<_CharT>(*this); } }; // A sink with an internal buffer. This is used to implement concrete sinks. template<typename _CharT> class _Buf_sink : public _Sink<_CharT> { protected: _CharT _M_buf[32 * sizeof(void*) / sizeof(_CharT)]; [[__gnu__::__always_inline__]] constexpr _Buf_sink() noexcept : _Sink<_CharT>(_M_buf) { } }; using _GLIBCXX_STD_C::vector; // A sink that fills a sequence (e.g. std::string, std::vector, std::deque). // Writes to a buffer then appends that to the sequence when it fills up. template<typename _Seq> class _Seq_sink final : public _Buf_sink<typename _Seq::value_type> { using _CharT = typename _Seq::value_type; _Seq _M_seq; // Transfer buffer contents to the sequence, so buffer can be refilled. void _M_overflow() override { auto __s = this->_M_used(); if (__s.empty()) [[unlikely]] return; // Nothing in the buffer to transfer to _M_seq. // If _M_reserve was called then _M_bump must have been called too. _GLIBCXX_DEBUG_ASSERT(__s.data() != _M_seq.data()); if constexpr (__is_specialization_of<_Seq, basic_string>) _M_seq.append(__s.data(), __s.size()); else _M_seq.insert(_M_seq.end(), __s.begin(), __s.end()); // Make the whole of _M_buf available for the next write: this->_M_rewind(); } typename _Sink<_CharT>::_Reservation _M_reserve(size_t __n) override { // We might already have n characters available in this->_M_unused(), // but the whole point of this function is to be an optimization for // the std::format("{}", x) case. We want to avoid writing to _M_buf // and then copying that into a basic_string if possible, so this // function prefers to create space directly in _M_seq rather than // using _M_buf. if constexpr (__is_specialization_of<_Seq, basic_string> || __is_specialization_of<_Seq, vector>) { // Flush the buffer to _M_seq first (should not be needed). if (this->_M_used().size()) [[unlikely]] _Seq_sink::_M_overflow(); // Expand _M_seq to make __n new characters available: const auto __sz = _M_seq.size(); if constexpr (is_same_v<string, _Seq> || is_same_v<wstring, _Seq>) _M_seq.__resize_and_overwrite(__sz + __n, [](auto, auto __n2) { return __n2; }); else _M_seq.resize(__sz + __n); // Set _M_used() to be a span over the original part of _M_seq // and _M_unused() to be the extra capacity we just created: this->_M_reset(_M_seq, __sz); return { this }; } else // Try to use the base class' buffer. return _Sink<_CharT>::_M_reserve(__n); } void _M_bump(size_t __n) override { if constexpr (__is_specialization_of<_Seq, basic_string> || __is_specialization_of<_Seq, vector>) { auto __s = this->_M_used(); _GLIBCXX_DEBUG_ASSERT(__s.data() == _M_seq.data()); // Truncate the sequence to the part that was actually written to: _M_seq.resize(__s.size() + __n); // Switch back to using buffer: this->_M_reset(this->_M_buf); } } public: // TODO: for SSO string, use SSO buffer as initial span, then switch // to _M_buf if it overflows? Or even do that for all unused capacity? [[__gnu__::__always_inline__]] _Seq_sink() noexcept(is_nothrow_default_constructible_v<_Seq>) { } _Seq_sink(_Seq&& __s) noexcept(is_nothrow_move_constructible_v<_Seq>) : _M_seq(std::move(__s)) { } using _Sink<_CharT>::out; _Seq get() && { if (this->_M_used().size() != 0) _Seq_sink::_M_overflow(); return std::move(_M_seq); } // A writable span that views everything written to the sink. // Will be either a view over _M_seq or the used part of _M_buf. span<_CharT> view() { auto __s = this->_M_used(); if (_M_seq.size()) { if (__s.size() != 0) _Seq_sink::_M_overflow(); return _M_seq; } return __s; } }; template<typename _CharT, typename _Alloc = allocator<_CharT>> using _Str_sink = _Seq_sink<basic_string<_CharT, char_traits<_CharT>, _Alloc>>; // template<typename _CharT, typename _Alloc = allocator<_CharT>> // using _Vec_sink = _Seq_sink<vector<_CharT, _Alloc>>; // A sink that writes to an output iterator. // Writes to a fixed-size buffer and then flushes to the output iterator // when the buffer fills up. template<typename _CharT, typename _OutIter> class _Iter_sink : public _Buf_sink<_CharT> { _OutIter _M_out; iter_difference_t<_OutIter> _M_max; protected: size_t _M_count = 0; void _M_overflow() override { auto __s = this->_M_used(); if (_M_max < 0) // No maximum. _M_out = ranges::copy(__s, std::move(_M_out)).out; else if (_M_count < static_cast<size_t>(_M_max)) { auto __max = _M_max - _M_count; span<_CharT> __first; if (__max < __s.size()) __first = __s.first(static_cast<size_t>(__max)); else __first = __s; _M_out = ranges::copy(__first, std::move(_M_out)).out; } this->_M_rewind(); _M_count += __s.size(); } public: [[__gnu__::__always_inline__]] explicit _Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __max = -1) : _M_out(std::move(__out)), _M_max(__max) { } using _Sink<_CharT>::out; format_to_n_result<_OutIter> _M_finish() && { if (this->_M_used().size() != 0) _Iter_sink::_M_overflow(); iter_difference_t<_OutIter> __count(_M_count); return { std::move(_M_out), __count }; } }; // Partial specialization for contiguous iterators. // No buffer is used, characters are written straight to the iterator. // We do not know the size of the output range, so the span size just grows // as needed. The end of the span might be an invalid pointer outside the // valid range, but we never actually call _M_span.end(). This class does // not introduce any invalid pointer arithmetic or overflows that would not // have happened anyway. template<typename _CharT, contiguous_iterator _OutIter> requires same_as<iter_value_t<_OutIter>, _CharT> class _Iter_sink<_CharT, _OutIter> : public _Sink<_CharT> { _OutIter _M_first; iter_difference_t<_OutIter> _M_max = -1; protected: size_t _M_count = 0; private: _CharT _M_buf[64]; // Write here after outputting _M_max characters. protected: void _M_overflow() override { if (this->_M_unused().size() != 0) return; // No need to switch to internal buffer yet. auto __s = this->_M_used(); if (_M_max >= 0) { _M_count += __s.size(); // Span was already sized for the maximum character count, // if it overflows then any further output must go to the // internal buffer, to be discarded. this->_M_reset(this->_M_buf); } else { // No maximum character count. Just extend the span to allow // writing more characters to it. this->_M_reset({__s.data(), __s.size() + 1024}, __s.size()); } } typename _Sink<_CharT>::_Reservation _M_reserve(size_t __n) final { auto __avail = this->_M_unused(); if (__n > __avail.size()) { if (_M_max >= 0) return {}; // cannot grow auto __s = this->_M_used(); this->_M_reset({__s.data(), __s.size() + __n}, __s.size()); } return { this }; } private: static span<_CharT> _S_make_span(_CharT* __ptr, iter_difference_t<_OutIter> __n, span<_CharT> __buf) noexcept { if (__n == 0) return __buf; // Only write to the internal buffer. if consteval { return {__ptr, 1}; } if (__n > 0) { if constexpr (!is_integral_v<iter_difference_t<_OutIter>> || sizeof(__n) > sizeof(size_t)) { // __int128 or __detail::__max_diff_type auto __m = iter_difference_t<_OutIter>((size_t)-1); if (__n > __m) __n = __m; } return {__ptr, (size_t)__n}; } #if __has_builtin(__builtin_dynamic_object_size) if (size_t __bytes = __builtin_dynamic_object_size(__ptr, 2)) return {__ptr, __bytes / sizeof(_CharT)}; #endif // Avoid forming a pointer to a different memory page. const auto __off = reinterpret_cast<__UINTPTR_TYPE__>(__ptr) % 1024; __n = (1024 - __off) / sizeof(_CharT); if (__n > 0) [[likely]] return {__ptr, static_cast<size_t>(__n)}; else // Misaligned/packed buffer of wchar_t? return {__ptr, 1}; } public: explicit _Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __n = -1) noexcept : _Sink<_CharT>(_S_make_span(std::to_address(__out), __n, _M_buf)), _M_first(__out), _M_max(__n) { } format_to_n_result<_OutIter> _M_finish() && { auto __s = this->_M_used(); if (__s.data() == _M_buf) { // Switched to internal buffer, so must have written _M_max. iter_difference_t<_OutIter> __count(_M_count + __s.size()); return { _M_first + _M_max, __count }; } else // Not using internal buffer yet { iter_difference_t<_OutIter> __count(__s.size()); return { _M_first + __count, __count }; } } }; enum _Arg_t : unsigned char { _Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull, _Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle, _Arg_i128, _Arg_u128, _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, // These are unused. #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT _Arg_next_value_, _Arg_f128 = _Arg_ldbl, _Arg_ibm128 = _Arg_next_value_, #else _Arg_f128, #endif _Arg_max_ }; template<typename _Context> struct _Arg_value { using _CharT = typename _Context::char_type; struct _HandleBase { const void* _M_ptr; void (*_M_func)(); }; union { monostate _M_none; bool _M_bool; _CharT _M_c; int _M_i; unsigned _M_u; long long _M_ll; unsigned long long _M_ull; float _M_flt; double _M_dbl; #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // No long double if it's ambiguous. long double _M_ldbl; #endif const _CharT* _M_str; basic_string_view<_CharT> _M_sv; const void* _M_ptr; _HandleBase _M_handle; #ifdef __SIZEOF_INT128__ __int128 _M_i128; unsigned __int128 _M_u128; #endif #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT __ieee128 _M_f128; __ibm128 _M_ibm128; #elif _GLIBCXX_FORMAT_F128 == 2 __float128_t _M_f128; #endif }; [[__gnu__::__always_inline__]] _Arg_value() : _M_none() { } #if 0 template<typename _Tp> _Arg_value(in_place_type_t<_Tp>, _Tp __val) { _S_get<_Tp>() = __val; } #endif template<typename _Tp, typename _Self> [[__gnu__::__always_inline__]] static auto& _S_get(_Self& __u) noexcept { if constexpr (is_same_v<_Tp, bool>) return __u._M_bool; else if constexpr (is_same_v<_Tp, _CharT>) return __u._M_c; else if constexpr (is_same_v<_Tp, int>) return __u._M_i; else if constexpr (is_same_v<_Tp, unsigned>) return __u._M_u; else if constexpr (is_same_v<_Tp, long long>) return __u._M_ll; else if constexpr (is_same_v<_Tp, unsigned long long>) return __u._M_ull; else if constexpr (is_same_v<_Tp, float>) return __u._M_flt; else if constexpr (is_same_v<_Tp, double>) return __u._M_dbl; #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT else if constexpr (is_same_v<_Tp, long double>) return __u._M_ldbl; #else else if constexpr (is_same_v<_Tp, __ieee128>) return __u._M_f128; else if constexpr (is_same_v<_Tp, __ibm128>) return __u._M_ibm128; #endif else if constexpr (is_same_v<_Tp, const _CharT*>) return __u._M_str; else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) return __u._M_sv; else if constexpr (is_same_v<_Tp, const void*>) return __u._M_ptr; #ifdef __SIZEOF_INT128__ else if constexpr (is_same_v<_Tp, __int128>) return __u._M_i128; else if constexpr (is_same_v<_Tp, unsigned __int128>) return __u._M_u128; #endif #if _GLIBCXX_FORMAT_F128 == 2 else if constexpr (is_same_v<_Tp, __float128_t>) return __u._M_f128; #endif else if constexpr (derived_from<_Tp, _HandleBase>) return static_cast<_Tp&>(__u._M_handle); // Otherwise, ill-formed. } template<typename _Self, typename _Tp> [[__gnu__::__always_inline__]] static void _S_set(_Self& __u, _Tp const& __v) noexcept { if constexpr (is_same_v<_Tp, bool>) __u._M_bool = __v; else if constexpr (is_same_v<_Tp, _CharT>) __u._M_c = __v; else if constexpr (is_same_v<_Tp, int>) __u._M_i = __v; else if constexpr (is_same_v<_Tp, unsigned>) __u._M_u = __v; else if constexpr (is_same_v<_Tp, long long>) __u._M_ll = __v; else if constexpr (is_same_v<_Tp, unsigned long long>) __u._M_ull = __v; else if constexpr (is_same_v<_Tp, float>) __u._M_flt = __v; else if constexpr (is_same_v<_Tp, double>) __u._M_dbl = __v; #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT else if constexpr (is_same_v<_Tp, long double>) __u._M_ldbl = __v; #else else if constexpr (is_same_v<_Tp, __ieee128>) __u._M_f128 = __v; else if constexpr (is_same_v<_Tp, __ibm128>) __u._M_ibm128 = __v; #endif else if constexpr (is_same_v<_Tp, const _CharT*>) __u._M_str = __v; else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) __u._M_sv = __v; else if constexpr (is_same_v<_Tp, const void*>) __u._M_ptr = __v; #ifdef __SIZEOF_INT128__ else if constexpr (is_same_v<_Tp, __int128>) __u._M_i128 = __v; else if constexpr (is_same_v<_Tp, unsigned __int128>) __u._M_u128 = __v; #endif #if _GLIBCXX_FORMAT_F128 == 2 else if constexpr (is_same_v<_Tp, __float128_t>) __u._M_f128 = __v; #endif else if constexpr (derived_from<_Tp, _HandleBase>) static_assert(false); // Otherwise, ill-formed. } template<typename _Tp> [[__gnu__::__always_inline__]] auto& _M_get() noexcept { return _S_get<_Tp>(*this); } template<typename _Tp> [[__gnu__::__always_inline__]] const auto& _M_get() const noexcept { return _S_get<_Tp>(*this); } template<typename _Tp> [[__gnu__::__always_inline__]] void _M_set(_Tp __v) noexcept { if constexpr (derived_from<_Tp, _HandleBase>) std::construct_at(&_M_handle, __v); else _S_set(*this, __v); } }; // [format.arg.store], class template format-arg-store template<typename _Context, typename... _Args> class _Arg_store; template<typename _Ch, typename _Tp> consteval _Arg_t __to_arg_t_enum() noexcept; } // namespace __format /// @endcond template<typename _Context> class basic_format_arg { using _CharT = typename _Context::char_type; template<typename _Tp> static constexpr bool __formattable = __format::__formattable_with<_Tp, _Context>; public: class handle : public __format::_Arg_value<_Context>::_HandleBase { using _Base = typename __format::_Arg_value<_Context>::_HandleBase; // Format as const if possible, to reduce instantiations. template<typename _Tp> using __maybe_const_t = __conditional_t<__formattable<const _Tp>, const _Tp, _Tp>; template<typename _Tq> static void _S_format(basic_format_parse_context<_CharT>& __parse_ctx, _Context& __format_ctx, const void* __ptr) { using _Td = remove_const_t<_Tq>; typename _Context::template formatter_type<_Td> __f; __parse_ctx.advance_to(__f.parse(__parse_ctx)); _Tq& __val = *const_cast<_Tq*>(static_cast<const _Td*>(__ptr)); __format_ctx.advance_to(__f.format(__val, __format_ctx)); } template<typename _Tp> explicit handle(_Tp& __val) noexcept { this->_M_ptr = __builtin_addressof(__val); auto __func = _S_format<__maybe_const_t<_Tp>>; this->_M_func = reinterpret_cast<void(*)()>(__func); } friend class basic_format_arg<_Context>; public: handle(const handle&) = default; handle& operator=(const handle&) = default; [[__gnu__::__always_inline__]] void format(basic_format_parse_context<_CharT>& __pc, _Context& __fc) const { using _Func = void(*)(basic_format_parse_context<_CharT>&, _Context&, const void*); auto __f = reinterpret_cast<_Func>(this->_M_func); __f(__pc, __fc, this->_M_ptr); } }; [[__gnu__::__always_inline__]] basic_format_arg() noexcept : _M_type(__format::_Arg_none) { } [[nodiscard,__gnu__::__always_inline__]] explicit operator bool() const noexcept { return _M_type != __format::_Arg_none; } #if __cpp_lib_format >= 202306L // >= C++26 template<typename _Visitor> decltype(auto) visit(this basic_format_arg __arg, _Visitor&& __vis) { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); } template<typename _Res, typename _Visitor> _Res visit(this basic_format_arg __arg, _Visitor&& __vis) { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); } #endif private: template<typename _Ctx> friend class basic_format_args; template<typename _Ctx, typename... _Args> friend class __format::_Arg_store; static_assert(is_trivially_copyable_v<__format::_Arg_value<_Context>>); __format::_Arg_value<_Context> _M_val; __format::_Arg_t _M_type; // Transform incoming argument type to the type stored in _Arg_value. // e.g. short -> int, std::string -> std::string_view, // char[3] -> const char*. template<typename _Tp> static consteval auto _S_to_arg_type() { using _Td = remove_const_t<_Tp>; if constexpr (is_same_v<_Td, bool>) return type_identity<bool>(); else if constexpr (is_same_v<_Td, _CharT>) return type_identity<_CharT>(); else if constexpr (is_same_v<_Td, char> && is_same_v<_CharT, wchar_t>) return type_identity<_CharT>(); #ifdef __SIZEOF_INT128__ // Check before signed/unsigned integer else if constexpr (is_same_v<_Td, __int128>) return type_identity<__int128>(); else if constexpr (is_same_v<_Td, unsigned __int128>) return type_identity<unsigned __int128>(); #endif else if constexpr (__is_signed_integer<_Td>::value) { if constexpr (sizeof(_Td) <= sizeof(int)) return type_identity<int>(); else if constexpr (sizeof(_Td) <= sizeof(long long)) return type_identity<long long>(); } else if constexpr (__is_unsigned_integer<_Td>::value) { if constexpr (sizeof(_Td) <= sizeof(unsigned)) return type_identity<unsigned>(); else if constexpr (sizeof(_Td) <= sizeof(unsigned long long)) return type_identity<unsigned long long>(); } else if constexpr (is_same_v<_Td, float>) return type_identity<float>(); else if constexpr (is_same_v<_Td, double>) return type_identity<double>(); #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT else if constexpr (is_same_v<_Td, long double>) return type_identity<long double>(); #else else if constexpr (is_same_v<_Td, __ibm128>) return type_identity<__ibm128>(); else if constexpr (is_same_v<_Td, __ieee128>) return type_identity<__ieee128>(); #endif #if defined(__FLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) else if constexpr (is_same_v<_Td, _Float16>) return type_identity<float>(); #endif #if defined(__BFLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) else if constexpr (is_same_v<_Td, decltype(0.0bf16)>) return type_identity<float>(); #endif #ifdef __FLT32_DIG__ else if constexpr (is_same_v<_Td, _Float32>) # ifdef _GLIBCXX_FLOAT_IS_IEEE_BINARY32 return type_identity<float>(); # else return type_identity<_Float32>(); # endif #endif #ifdef __FLT64_DIG__ else if constexpr (is_same_v<_Td, _Float64>) # ifdef _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 return type_identity<double>(); # else return type_identity<_Float64>(); # endif #endif #if _GLIBCXX_FORMAT_F128 # if __FLT128_DIG__ else if constexpr (is_same_v<_Td, _Float128>) return type_identity<__format::__float128_t>(); # endif # if __SIZEOF_FLOAT128__ else if constexpr (is_same_v<_Td, __float128>) return type_identity<__format::__float128_t>(); # endif #endif else if constexpr (__is_specialization_of<_Td, basic_string_view> || __is_specialization_of<_Td, basic_string>) { if constexpr (is_same_v<typename _Td::value_type, _CharT>) return type_identity<basic_string_view<_CharT>>(); else return type_identity<handle>(); } else if constexpr (is_same_v<decay_t<_Td>, const _CharT*>) return type_identity<const _CharT*>(); else if constexpr (is_same_v<decay_t<_Td>, _CharT*>) return type_identity<const _CharT*>(); else if constexpr (is_void_v<remove_pointer_t<_Td>>) return type_identity<const void*>(); else if constexpr (is_same_v<_Td, nullptr_t>) return type_identity<const void*>(); else return type_identity<handle>(); } // Transform a formattable type to the appropriate storage type. template<typename _Tp> using _Normalize = typename decltype(_S_to_arg_type<_Tp>())::type; // Get the _Arg_t value corresponding to a normalized type. template<typename _Tp> static consteval __format::_Arg_t _S_to_enum() { using namespace __format; if constexpr (is_same_v<_Tp, bool>) return _Arg_bool; else if constexpr (is_same_v<_Tp, _CharT>) return _Arg_c; else if constexpr (is_same_v<_Tp, int>) return _Arg_i; else if constexpr (is_same_v<_Tp, unsigned>) return _Arg_u; else if constexpr (is_same_v<_Tp, long long>) return _Arg_ll; else if constexpr (is_same_v<_Tp, unsigned long long>) return _Arg_ull; else if constexpr (is_same_v<_Tp, float>) return _Arg_flt; else if constexpr (is_same_v<_Tp, double>) return _Arg_dbl; #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT else if constexpr (is_same_v<_Tp, long double>) return _Arg_ldbl; #else // Don't use _Arg_ldbl for this target, it's ambiguous. else if constexpr (is_same_v<_Tp, __ibm128>) return _Arg_ibm128; else if constexpr (is_same_v<_Tp, __ieee128>) return _Arg_f128; #endif else if constexpr (is_same_v<_Tp, const _CharT*>) return _Arg_str; else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) return _Arg_sv; else if constexpr (is_same_v<_Tp, const void*>) return _Arg_ptr; #ifdef __SIZEOF_INT128__ else if constexpr (is_same_v<_Tp, __int128>) return _Arg_i128; else if constexpr (is_same_v<_Tp, unsigned __int128>) return _Arg_u128; #endif // N.B. some of these types will never actually be used here, // because they get normalized to a standard floating-point type. #if defined __FLT32_DIG__ && ! _GLIBCXX_FLOAT_IS_IEEE_BINARY32 else if constexpr (is_same_v<_Tp, _Float32>) return _Arg_f32; #endif #if defined __FLT64_DIG__ && ! _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 else if constexpr (is_same_v<_Tp, _Float64>) return _Arg_f64; #endif #if _GLIBCXX_FORMAT_F128 == 2 else if constexpr (is_same_v<_Tp, __format::__float128_t>) return _Arg_f128; #endif else if constexpr (is_same_v<_Tp, handle>) return _Arg_handle; } template<typename _Tp> void _M_set(_Tp __v) noexcept { _M_type = _S_to_enum<_Tp>(); _M_val._M_set(__v); } template<typename _Tp> requires __format::__formattable_with<_Tp, _Context> explicit basic_format_arg(_Tp& __v) noexcept { using _Td = _Normalize<_Tp>; if constexpr (is_same_v<_Td, basic_string_view<_CharT>>) _M_set(_Td{__v.data(), __v.size()}); else if constexpr (is_same_v<remove_const_t<_Tp>, char> && is_same_v<_CharT, wchar_t>) _M_set(static_cast<_Td>(static_cast<unsigned char>(__v))); else _M_set(static_cast<_Td>(__v)); } template<typename _Ctx, typename... _Argz> friend auto make_format_args(_Argz&...) noexcept; template<typename _Visitor, typename _Ctx> friend decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>); template<typename _Ch, typename _Tp> friend consteval __format::_Arg_t __format::__to_arg_t_enum() noexcept; template<typename _Visitor> decltype(auto) _M_visit(_Visitor&& __vis, __format::_Arg_t __type) { using namespace __format; switch (__type) { case _Arg_none: return std::forward<_Visitor>(__vis)(_M_val._M_none); case _Arg_bool: return std::forward<_Visitor>(__vis)(_M_val._M_bool); case _Arg_c: return std::forward<_Visitor>(__vis)(_M_val._M_c); case _Arg_i: return std::forward<_Visitor>(__vis)(_M_val._M_i); case _Arg_u: return std::forward<_Visitor>(__vis)(_M_val._M_u); case _Arg_ll: return std::forward<_Visitor>(__vis)(_M_val._M_ll); case _Arg_ull: return std::forward<_Visitor>(__vis)(_M_val._M_ull); #if __glibcxx_to_chars // FIXME: need to be able to format these types! case _Arg_flt: return std::forward<_Visitor>(__vis)(_M_val._M_flt); case _Arg_dbl: return std::forward<_Visitor>(__vis)(_M_val._M_dbl); #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT case _Arg_ldbl: return std::forward<_Visitor>(__vis)(_M_val._M_ldbl); #else case _Arg_f128: return std::forward<_Visitor>(__vis)(_M_val._M_f128); case _Arg_ibm128: return std::forward<_Visitor>(__vis)(_M_val._M_ibm128); #endif #endif case _Arg_str: return std::forward<_Visitor>(__vis)(_M_val._M_str); case _Arg_sv: return std::forward<_Visitor>(__vis)(_M_val._M_sv); case _Arg_ptr: return std::forward<_Visitor>(__vis)(_M_val._M_ptr); case _Arg_handle: { auto& __h = static_cast<handle&>(_M_val._M_handle); return std::forward<_Visitor>(__vis)(__h); } #ifdef __SIZEOF_INT128__ case _Arg_i128: return std::forward<_Visitor>(__vis)(_M_val._M_i128); case _Arg_u128: return std::forward<_Visitor>(__vis)(_M_val._M_u128); #endif #if _GLIBCXX_FORMAT_F128 == 2 case _Arg_f128: return std::forward<_Visitor>(__vis)(_M_val._M_f128); #endif default: // _Arg_f16 etc. __builtin_unreachable(); } } }; template<typename _Visitor, typename _Context> _GLIBCXX26_DEPRECATED_SUGGEST("std::basic_format_arg::visit") inline decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); } /// @cond undocumented namespace __format { struct _WidthPrecVisitor { template<typename _Tp> size_t operator()(_Tp& __arg) const { if constexpr (is_same_v<_Tp, monostate>) __format::__invalid_arg_id_in_format_string(); // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3720. Restrict the valid types of arg-id for width and precision // 3721. Allow an arg-id with a value of zero for width else if constexpr (sizeof(_Tp) <= sizeof(long long)) { // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3720. Restrict the valid types of arg-id for width and precision if constexpr (__is_unsigned_integer<_Tp>::value) return __arg; else if constexpr (__is_signed_integer<_Tp>::value) if (__arg >= 0) return __arg; } __throw_format_error("format error: argument used for width or " "precision must be a non-negative integer"); } }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" template<typename _Context> inline size_t __int_from_arg(const basic_format_arg<_Context>& __arg) { return std::visit_format_arg(_WidthPrecVisitor(), __arg); } // Pack _Arg_t enum values into a single 60-bit integer. template<int _Bits, size_t _Nm> constexpr auto __pack_arg_types(const array<_Arg_t, _Nm>& __types) { __UINT64_TYPE__ __packed_types = 0; for (auto __i = __types.rbegin(); __i != __types.rend(); ++__i) __packed_types = (__packed_types << _Bits) | *__i; return __packed_types; } } // namespace __format /// @endcond template<typename _Context> class basic_format_args { static constexpr int _S_packed_type_bits = 5; // _Arg_t values [0,20] static constexpr int _S_packed_type_mask = 0b11111; static constexpr int _S_max_packed_args = 12; static_assert( __format::_Arg_max_ <= (1 << _S_packed_type_bits) ); template<typename... _Args> using _Store = __format::_Arg_store<_Context, _Args...>; template<typename _Ctx, typename... _Args> friend class __format::_Arg_store; using uint64_t = __UINT64_TYPE__; using _Format_arg = basic_format_arg<_Context>; using _Format_arg_val = __format::_Arg_value<_Context>; // If args are packed then the number of args is in _M_packed_size and // the packed types are in _M_unpacked_size, accessed via _M_type(i). // If args are not packed then the number of args is in _M_unpacked_size // and _M_packed_size is zero. uint64_t _M_packed_size : 4; uint64_t _M_unpacked_size : 60; union { const _Format_arg_val* _M_values; // Active when _M_packed_size != 0 const _Format_arg* _M_args; // Active when _M_packed_size == 0 }; size_t _M_size() const noexcept { return _M_packed_size ? _M_packed_size : _M_unpacked_size; } typename __format::_Arg_t _M_type(size_t __i) const noexcept { uint64_t __t = _M_unpacked_size >> (__i * _S_packed_type_bits); return static_cast<__format::_Arg_t>(__t & _S_packed_type_mask); } template<typename _Ctx, typename... _Args> friend auto make_format_args(_Args&...) noexcept; // An array of _Arg_t enums corresponding to _Args... template<typename... _Args> static consteval array<__format::_Arg_t, sizeof...(_Args)> _S_types_to_pack() { return {_Format_arg::template _S_to_enum<_Args>()...}; } public: template<typename... _Args> basic_format_args(const _Store<_Args...>& __store) noexcept; [[nodiscard,__gnu__::__always_inline__]] basic_format_arg<_Context> get(size_t __i) const noexcept { basic_format_arg<_Context> __arg; if (__i < _M_packed_size) { __arg._M_type = _M_type(__i); __arg._M_val = _M_values[__i]; } else if (_M_packed_size == 0 && __i < _M_unpacked_size) __arg = _M_args[__i]; return __arg; } }; // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3810. CTAD for std::basic_format_args template<typename _Context, typename... _Args> basic_format_args(__format::_Arg_store<_Context, _Args...>) -> basic_format_args<_Context>; template<typename _Context, typename... _Args> auto make_format_args(_Args&... __fmt_args) noexcept; // An array of type-erased formatting arguments. template<typename _Context, typename... _Args> class __format::_Arg_store { friend std::basic_format_args<_Context>; template<typename _Ctx, typename... _Argz> friend auto std:: #if _GLIBCXX_INLINE_VERSION __8:: // Needed for PR c++/59256 #endif make_format_args(_Argz&...) noexcept; // For a sufficiently small number of arguments we only store values. // basic_format_args can get the types from the _Args pack. static constexpr bool _S_values_only = sizeof...(_Args) <= basic_format_args<_Context>::_S_max_packed_args; using _Element_t = __conditional_t<_S_values_only, __format::_Arg_value<_Context>, basic_format_arg<_Context>>; _Element_t _M_args[sizeof...(_Args)]; template<typename _Tp> static _Element_t _S_make_elt(_Tp& __v) { using _Tq = remove_const_t<_Tp>; using _CharT = typename _Context::char_type; static_assert(is_default_constructible_v<formatter<_Tq, _CharT>>, "std::formatter must be specialized for the type " "of each format arg"); using __format::__formattable_with; if constexpr (is_const_v<_Tp>) if constexpr (!__formattable_with<_Tp, _Context>) if constexpr (__formattable_with<_Tq, _Context>) static_assert(__formattable_with<_Tp, _Context>, "format arg must be non-const because its " "std::formatter specialization has a " "non-const reference parameter"); basic_format_arg<_Context> __arg(__v); if constexpr (_S_values_only) return __arg._M_val; else return __arg; } template<typename... _Tp> requires (sizeof...(_Tp) == sizeof...(_Args)) [[__gnu__::__always_inline__]] _Arg_store(_Tp&... __a) noexcept : _M_args{_S_make_elt(__a)...} { } }; template<typename _Context> class __format::_Arg_store<_Context> { }; template<typename _Context> template<typename... _Args> inline basic_format_args<_Context>:: basic_format_args(const _Store<_Args...>& __store) noexcept { if constexpr (sizeof...(_Args) == 0) { _M_packed_size = 0; _M_unpacked_size = 0; _M_args = nullptr; } else if constexpr (sizeof...(_Args) <= _S_max_packed_args) { // The number of packed arguments: _M_packed_size = sizeof...(_Args); // The packed type enums: _M_unpacked_size = __format::__pack_arg_types<_S_packed_type_bits>(_S_types_to_pack<_Args...>()); // The _Arg_value objects. _M_values = __store._M_args; } else { // No packed arguments: _M_packed_size = 0; // The number of unpacked arguments: _M_unpacked_size = sizeof...(_Args); // The basic_format_arg objects: _M_args = __store._M_args; } } /// Capture formatting arguments for use by `std::vformat`. template<typename _Context = format_context, typename... _Args> [[nodiscard,__gnu__::__always_inline__]] inline auto make_format_args(_Args&... __fmt_args) noexcept { using _Fmt_arg = basic_format_arg<_Context>; using _Store = __format::_Arg_store<_Context, typename _Fmt_arg::template _Normalize<_Args>...>; return _Store(__fmt_args...); } #ifdef _GLIBCXX_USE_WCHAR_T /// Capture formatting arguments for use by `std::vformat` (for wide output). template<typename... _Args> [[nodiscard,__gnu__::__always_inline__]] inline auto make_wformat_args(_Args&... __args) noexcept { return std::make_format_args<wformat_context>(__args...); } #endif /// @cond undocumented namespace __format { template<typename _Out, typename _CharT, typename _Context> _Out __do_vformat_to(_Out, basic_string_view<_CharT>, const basic_format_args<_Context>&, const locale* = nullptr); template<typename _CharT> struct __formatter_chrono; } // namespace __format /// @endcond /** Context for std::format and similar functions. * * A formatting context contains an output iterator and locale to use * for the formatting operations. Most programs will never need to use * this class template explicitly. For typical uses of `std::format` the * library will use the specializations `std::format_context` (for `char`) * and `std::wformat_context` (for `wchar_t`). * * You are not allowed to define partial or explicit specializations of * this class template. * * @since C++20 */ template<typename _Out, typename _CharT> class basic_format_context { static_assert( output_iterator<_Out, const _CharT&> ); basic_format_args<basic_format_context> _M_args; _Out _M_out; __format::_Optional_locale _M_loc; basic_format_context(basic_format_args<basic_format_context> __args, _Out __out) : _M_args(__args), _M_out(std::move(__out)) { } basic_format_context(basic_format_args<basic_format_context> __args, _Out __out, const std::locale& __loc) : _M_args(__args), _M_out(std::move(__out)), _M_loc(__loc) { } // _GLIBCXX_RESOLVE_LIB_DEFECTS // 4061. Should std::basic_format_context be // default-constructible/copyable/movable? basic_format_context(const basic_format_context&) = delete; basic_format_context& operator=(const basic_format_context&) = delete; template<typename _Out2, typename _CharT2, typename _Context2> friend _Out2 __format::__do_vformat_to(_Out2, basic_string_view<_CharT2>, const basic_format_args<_Context2>&, const locale*); friend __format::__formatter_chrono<_CharT>; public: ~basic_format_context() = default; using iterator = _Out; using char_type = _CharT; template<typename _Tp> using formatter_type = formatter<_Tp, _CharT>; [[nodiscard]] basic_format_arg<basic_format_context> arg(size_t __id) const noexcept { return _M_args.get(__id); } [[nodiscard]] std::locale locale() { return _M_loc.value(); } [[nodiscard]] iterator out() { return std::move(_M_out); } void advance_to(iterator __it) { _M_out = std::move(__it); } }; /// @cond undocumented namespace __format { // Abstract base class defining an interface for scanning format strings. // Scan the characters in a format string, dividing it up into strings of // ordinary characters, escape sequences, and replacement fields. // Call virtual functions for derived classes to parse format-specifiers // or write formatted output. template<typename _CharT> struct _Scanner { using iterator = typename basic_format_parse_context<_CharT>::iterator; struct _Parse_context : basic_format_parse_context<_CharT> { using basic_format_parse_context<_CharT>::basic_format_parse_context; const _Arg_t* _M_types = nullptr; } _M_pc; constexpr explicit _Scanner(basic_string_view<_CharT> __str, size_t __nargs = -1) : _M_pc(__str, __nargs) { } constexpr iterator begin() const noexcept { return _M_pc.begin(); } constexpr iterator end() const noexcept { return _M_pc.end(); } constexpr void _M_scan() { basic_string_view<_CharT> __fmt = _M_fmt_str(); if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}') { _M_pc.advance_to(begin() + 1); _M_format_arg(_M_pc.next_arg_id()); return; } size_t __lbr = __fmt.find('{'); size_t __rbr = __fmt.find('}'); while (__fmt.size()) { auto __cmp = __lbr <=> __rbr; if (__cmp == 0) { _M_on_chars(end()); _M_pc.advance_to(end()); return; } else if (__cmp < 0) { if (__lbr + 1 == __fmt.size() || (__rbr == __fmt.npos && __fmt[__lbr + 1] != '{')) __format::__unmatched_left_brace_in_format_string(); const bool __is_escape = __fmt[__lbr + 1] == '{'; iterator __last = begin() + __lbr + int(__is_escape); _M_on_chars(__last); _M_pc.advance_to(__last + 1); __fmt = _M_fmt_str(); if (__is_escape) { if (__rbr != __fmt.npos) __rbr -= __lbr + 2; __lbr = __fmt.find('{'); } else { _M_on_replacement_field(); __fmt = _M_fmt_str(); __lbr = __fmt.find('{'); __rbr = __fmt.find('}'); } } else { if (++__rbr == __fmt.size() || __fmt[__rbr] != '}') __format::__unmatched_right_brace_in_format_string(); iterator __last = begin() + __rbr; _M_on_chars(__last); _M_pc.advance_to(__last + 1); __fmt = _M_fmt_str(); if (__lbr != __fmt.npos) __lbr -= __rbr + 1; __rbr = __fmt.find('}'); } } } constexpr basic_string_view<_CharT> _M_fmt_str() const noexcept { return {begin(), end()}; } constexpr virtual void _M_on_chars(iterator) { } constexpr void _M_on_replacement_field() { auto __next = begin(); size_t __id; if (*__next == '}') __id = _M_pc.next_arg_id(); else if (*__next == ':') { __id = _M_pc.next_arg_id(); _M_pc.advance_to(++__next); } else { auto [__i, __ptr] = __format::__parse_arg_id(begin(), end()); if (!__ptr || !(*__ptr == '}' || *__ptr == ':')) __format::__invalid_arg_id_in_format_string(); _M_pc.check_arg_id(__id = __i); if (*__ptr == ':') { _M_pc.advance_to(++__ptr); } else _M_pc.advance_to(__ptr); } _M_format_arg(__id); if (begin() == end() || *begin() != '}') __format::__unmatched_left_brace_in_format_string(); _M_pc.advance_to(begin() + 1); // Move past '}' } constexpr virtual void _M_format_arg(size_t __id) = 0; }; // Process a format string and format the arguments in the context. template<typename _Out, typename _CharT> class _Formatting_scanner : public _Scanner<_CharT> { public: _Formatting_scanner(basic_format_context<_Out, _CharT>& __fc, basic_string_view<_CharT> __str) : _Scanner<_CharT>(__str), _M_fc(__fc) { } private: basic_format_context<_Out, _CharT>& _M_fc; using iterator = typename _Scanner<_CharT>::iterator; constexpr void _M_on_chars(iterator __last) override { basic_string_view<_CharT> __str(this->begin(), __last); _M_fc.advance_to(__format::__write(_M_fc.out(), __str)); } constexpr void _M_format_arg(size_t __id) override { using _Context = basic_format_context<_Out, _CharT>; using handle = typename basic_format_arg<_Context>::handle; std::visit_format_arg([this](auto& __arg) { using _Type = remove_reference_t<decltype(__arg)>; using _Formatter = typename _Context::template formatter_type<_Type>; if constexpr (is_same_v<_Type, monostate>) __format::__invalid_arg_id_in_format_string(); else if constexpr (is_same_v<_Type, handle>) __arg.format(this->_M_pc, this->_M_fc); else if constexpr (is_default_constructible_v<_Formatter>) { _Formatter __f; this->_M_pc.advance_to(__f.parse(this->_M_pc)); this->_M_fc.advance_to(__f.format(__arg, this->_M_fc)); } else static_assert(__format::__formattable_with<_Type, _Context>); }, _M_fc.arg(__id)); } }; template<typename _CharT, typename _Tp> consteval _Arg_t __to_arg_t_enum() noexcept { using _Context = __format::__format_context<_CharT>; using _Fmt_arg = basic_format_arg<_Context>; using _NormalizedTp = typename _Fmt_arg::template _Normalize<_Tp>; return _Fmt_arg::template _S_to_enum<_NormalizedTp>(); } // Validate a format string for Args. template<typename _CharT, typename... _Args> class _Checking_scanner : public _Scanner<_CharT> { static_assert( (is_default_constructible_v<formatter<_Args, _CharT>> && ...), "std::formatter must be specialized for each type being formatted"); public: consteval _Checking_scanner(basic_string_view<_CharT> __str) : _Scanner<_CharT>(__str, sizeof...(_Args)) { #if __cpp_lib_format >= 202305L this->_M_pc._M_types = _M_types.data(); #endif } private: constexpr void _M_format_arg(size_t __id) override { if constexpr (sizeof...(_Args) != 0) { if (__id < sizeof...(_Args)) { _M_parse_format_spec<_Args...>(__id); return; } } __builtin_unreachable(); } template<typename _Tp, typename... _OtherArgs> constexpr void _M_parse_format_spec(size_t __id) { if (__id == 0) { formatter<_Tp, _CharT> __f; this->_M_pc.advance_to(__f.parse(this->_M_pc)); } else if constexpr (sizeof...(_OtherArgs) != 0) _M_parse_format_spec<_OtherArgs...>(__id - 1); else __builtin_unreachable(); } #if __cpp_lib_format >= 202305L array<_Arg_t, sizeof...(_Args)> _M_types{ { __format::__to_arg_t_enum<_CharT, _Args>()... } }; #endif }; template<typename _Out, typename _CharT, typename _Context> inline _Out __do_vformat_to(_Out __out, basic_string_view<_CharT> __fmt, const basic_format_args<_Context>& __args, const locale* __loc) { _Iter_sink<_CharT, _Out> __sink(std::move(__out)); _Sink_iter<_CharT> __sink_out; if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) __sink_out = __out; // Already a sink iterator, safe to use post-move. else __sink_out = __sink.out(); if constexpr (is_same_v<_CharT, char>) // Fast path for "{}" format strings and simple format arg types. if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}') { bool __done = false; std::visit_format_arg([&](auto& __arg) { using _Tp = remove_cvref_t<decltype(__arg)>; if constexpr (is_same_v<_Tp, bool>) { size_t __len = 4 + !__arg; const char* __chars[] = { "false", "true" }; if (auto __res = __sink_out._M_reserve(__len)) { __builtin_memcpy(__res.get(), __chars[__arg], __len); __res._M_bump(__len); __done = true; } } else if constexpr (is_same_v<_Tp, char>) { if (auto __res = __sink_out._M_reserve(1)) { *__res.get() = __arg; __res._M_bump(1); __done = true; } } else if constexpr (is_integral_v<_Tp>) { make_unsigned_t<_Tp> __uval; const bool __neg = __arg < 0; if (__neg) __uval = make_unsigned_t<_Tp>(~__arg) + 1u; else __uval = __arg; const auto __n = __detail::__to_chars_len(__uval); if (auto __res = __sink_out._M_reserve(__n + __neg)) { auto __ptr = __res.get(); *__ptr = '-'; __detail::__to_chars_10_impl(__ptr + (int)__neg, __n, __uval); __res._M_bump(__n + __neg); __done = true; } } else if constexpr (is_convertible_v<_Tp, string_view>) { string_view __sv = __arg; if (auto __res = __sink_out._M_reserve(__sv.size())) { __builtin_memcpy(__res.get(), __sv.data(), __sv.size()); __res._M_bump(__sv.size()); __done = true; } } }, __args.get(0)); if (__done) { if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) return __sink_out; else return std::move(__sink)._M_finish().out; } } auto __ctx = __loc == nullptr ? _Context(__args, __sink_out) : _Context(__args, __sink_out, *__loc); _Formatting_scanner<_Sink_iter<_CharT>, _CharT> __scanner(__ctx, __fmt); __scanner._M_scan(); if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) return __ctx.out(); else return std::move(__sink)._M_finish().out; } #pragma GCC diagnostic pop } // namespace __format /// @endcond #if __cpp_lib_format >= 202305L template<typename _CharT> template<typename... _Ts> constexpr void basic_format_parse_context<_CharT>::check_dynamic_spec(size_t __id) noexcept { // This call enforces the Mandates: condition that _Ts contains valid // types and each type appears at most once. It could be a static_assert // but this way failures give better diagnostics, due to calling the // non-constexpr __invalid_dynamic_spec function. [[maybe_unused]] constexpr bool __ok = __check_dynamic_spec_types<_Ts...>(); if consteval { if (__id >= _M_num_args) __format::__invalid_arg_id_in_format_string(); if constexpr (sizeof...(_Ts) != 0) { using _Parse_context = __format::_Scanner<_CharT>::_Parse_context; auto _M_types = static_cast<_Parse_context*>(this)->_M_types; if (_M_types) { auto __arg = _M_types[__id]; __format::_Arg_t __types[] = { __format::__to_arg_t_enum<_CharT, _Ts>()... }; for (auto __t : __types) if (__arg == __t) return; __invalid_dynamic_spec("arg(id) type does not match"); } } } } #endif template<typename _CharT, typename... _Args> template<typename _Tp> requires convertible_to<const _Tp&, basic_string_view<_CharT>> consteval basic_format_string<_CharT, _Args...>:: basic_format_string(const _Tp& __s) : _M_str(__s) { __format::_Checking_scanner<_CharT, remove_cvref_t<_Args>...> __scanner(_M_str); __scanner._M_scan(); } // [format.functions], formatting functions template<typename _Out> requires output_iterator<_Out, const char&> [[__gnu__::__always_inline__]] inline _Out vformat_to(_Out __out, string_view __fmt, format_args __args) { return __format::__do_vformat_to(std::move(__out), __fmt, __args); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out> requires output_iterator<_Out, const wchar_t&> [[__gnu__::__always_inline__]] inline _Out vformat_to(_Out __out, wstring_view __fmt, wformat_args __args) { return __format::__do_vformat_to(std::move(__out), __fmt, __args); } #endif template<typename _Out> requires output_iterator<_Out, const char&> [[__gnu__::__always_inline__]] inline _Out vformat_to(_Out __out, const locale& __loc, string_view __fmt, format_args __args) { return __format::__do_vformat_to(std::move(__out), __fmt, __args, &__loc); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out> requires output_iterator<_Out, const wchar_t&> [[__gnu__::__always_inline__]] inline _Out vformat_to(_Out __out, const locale& __loc, wstring_view __fmt, wformat_args __args) { return __format::__do_vformat_to(std::move(__out), __fmt, __args, &__loc); } #endif [[nodiscard]] inline string vformat(string_view __fmt, format_args __args) { __format::_Str_sink<char> __buf; std::vformat_to(__buf.out(), __fmt, __args); return std::move(__buf).get(); } #ifdef _GLIBCXX_USE_WCHAR_T [[nodiscard]] inline wstring vformat(wstring_view __fmt, wformat_args __args) { __format::_Str_sink<wchar_t> __buf; std::vformat_to(__buf.out(), __fmt, __args); return std::move(__buf).get(); } #endif [[nodiscard]] inline string vformat(const locale& __loc, string_view __fmt, format_args __args) { __format::_Str_sink<char> __buf; std::vformat_to(__buf.out(), __loc, __fmt, __args); return std::move(__buf).get(); } #ifdef _GLIBCXX_USE_WCHAR_T [[nodiscard]] inline wstring vformat(const locale& __loc, wstring_view __fmt, wformat_args __args) { __format::_Str_sink<wchar_t> __buf; std::vformat_to(__buf.out(), __loc, __fmt, __args); return std::move(__buf).get(); } #endif template<typename... _Args> [[nodiscard]] inline string format(format_string<_Args...> __fmt, _Args&&... __args) { return std::vformat(__fmt.get(), std::make_format_args(__args...)); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename... _Args> [[nodiscard]] inline wstring format(wformat_string<_Args...> __fmt, _Args&&... __args) { return std::vformat(__fmt.get(), std::make_wformat_args(__args...)); } #endif template<typename... _Args> [[nodiscard]] inline string format(const locale& __loc, format_string<_Args...> __fmt, _Args&&... __args) { return std::vformat(__loc, __fmt.get(), std::make_format_args(__args...)); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename... _Args> [[nodiscard]] inline wstring format(const locale& __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { return std::vformat(__loc, __fmt.get(), std::make_wformat_args(__args...)); } #endif template<typename _Out, typename... _Args> requires output_iterator<_Out, const char&> inline _Out format_to(_Out __out, format_string<_Args...> __fmt, _Args&&... __args) { return std::vformat_to(std::move(__out), __fmt.get(), std::make_format_args(__args...)); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out, typename... _Args> requires output_iterator<_Out, const wchar_t&> inline _Out format_to(_Out __out, wformat_string<_Args...> __fmt, _Args&&... __args) { return std::vformat_to(std::move(__out), __fmt.get(), std::make_wformat_args(__args...)); } #endif template<typename _Out, typename... _Args> requires output_iterator<_Out, const char&> inline _Out format_to(_Out __out, const locale& __loc, format_string<_Args...> __fmt, _Args&&... __args) { return std::vformat_to(std::move(__out), __loc, __fmt.get(), std::make_format_args(__args...)); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out, typename... _Args> requires output_iterator<_Out, const wchar_t&> inline _Out format_to(_Out __out, const locale& __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { return std::vformat_to(std::move(__out), __loc, __fmt.get(), std::make_wformat_args(__args...)); } #endif template<typename _Out, typename... _Args> requires output_iterator<_Out, const char&> inline format_to_n_result<_Out> format_to_n(_Out __out, iter_difference_t<_Out> __n, format_string<_Args...> __fmt, _Args&&... __args) { __format::_Iter_sink<char, _Out> __sink(std::move(__out), __n); std::vformat_to(__sink.out(), __fmt.get(), std::make_format_args(__args...)); return std::move(__sink)._M_finish(); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out, typename... _Args> requires output_iterator<_Out, const wchar_t&> inline format_to_n_result<_Out> format_to_n(_Out __out, iter_difference_t<_Out> __n, wformat_string<_Args...> __fmt, _Args&&... __args) { __format::_Iter_sink<wchar_t, _Out> __sink(std::move(__out), __n); std::vformat_to(__sink.out(), __fmt.get(), std::make_wformat_args(__args...)); return std::move(__sink)._M_finish(); } #endif template<typename _Out, typename... _Args> requires output_iterator<_Out, const char&> inline format_to_n_result<_Out> format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc, format_string<_Args...> __fmt, _Args&&... __args) { __format::_Iter_sink<char, _Out> __sink(std::move(__out), __n); std::vformat_to(__sink.out(), __loc, __fmt.get(), std::make_format_args(__args...)); return std::move(__sink)._M_finish(); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out, typename... _Args> requires output_iterator<_Out, const wchar_t&> inline format_to_n_result<_Out> format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { __format::_Iter_sink<wchar_t, _Out> __sink(std::move(__out), __n); std::vformat_to(__sink.out(), __loc, __fmt.get(), std::make_wformat_args(__args...)); return std::move(__sink)._M_finish(); } #endif /// @cond undocumented namespace __format { #if 1 template<typename _CharT> class _Counting_sink final : public _Iter_sink<_CharT, _CharT*> { public: _Counting_sink() : _Iter_sink<_CharT, _CharT*>(nullptr, 0) { } [[__gnu__::__always_inline__]] size_t count() const { return this->_M_count + this->_M_used().size(); } }; #else template<typename _CharT> class _Counting_sink : public _Buf_sink<_CharT> { size_t _M_count = 0; void _M_overflow() override { if (!std::is_constant_evaluated()) _M_count += this->_M_used().size(); this->_M_rewind(); } public: _Counting_sink() = default; [[__gnu__::__always_inline__]] size_t count() noexcept { _Counting_sink::_M_overflow(); return _M_count; } }; #endif } // namespace __format /// @endcond template<typename... _Args> [[nodiscard]] inline size_t formatted_size(format_string<_Args...> __fmt, _Args&&... __args) { __format::_Counting_sink<char> __buf; std::vformat_to(__buf.out(), __fmt.get(), std::make_format_args(__args...)); return __buf.count(); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename... _Args> [[nodiscard]] inline size_t formatted_size(wformat_string<_Args...> __fmt, _Args&&... __args) { __format::_Counting_sink<wchar_t> __buf; std::vformat_to(__buf.out(), __fmt.get(), std::make_wformat_args(__args...)); return __buf.count(); } #endif template<typename... _Args> [[nodiscard]] inline size_t formatted_size(const locale& __loc, format_string<_Args...> __fmt, _Args&&... __args) { __format::_Counting_sink<char> __buf; std::vformat_to(__buf.out(), __loc, __fmt.get(), std::make_format_args(__args...)); return __buf.count(); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename... _Args> [[nodiscard]] inline size_t formatted_size(const locale& __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { __format::_Counting_sink<wchar_t> __buf; std::vformat_to(__buf.out(), __loc, __fmt.get(), std::make_wformat_args(__args...)); return __buf.count(); } #endif #if __cpp_lib_format_ranges // [format.range], formatting of ranges // [format.range.fmtkind], variable template format_kind enum class range_format { disabled, map, set, sequence, string, debug_string }; /// @cond undocumented template<typename _Rg> constexpr auto format_kind = not defined(format_kind<_Rg>); template<typename _Tp> consteval range_format __fmt_kind() { using _Ref = ranges::range_reference_t<_Tp>; if constexpr (is_same_v<remove_cvref_t<_Ref>, _Tp>) return range_format::disabled; else if constexpr (requires { typename _Tp::key_type; }) { if constexpr (requires { typename _Tp::mapped_type; }) { using _Up = remove_cvref_t<_Ref>; if constexpr (__is_pair<_Up>) return range_format::map; else if constexpr (__is_specialization_of<_Up, tuple>) if constexpr (tuple_size_v<_Up> == 2) return range_format::map; } return range_format::set; } else return range_format::sequence; } /// @endcond /// A constant determining how a range should be formatted. template<ranges::input_range _Rg> requires same_as<_Rg, remove_cvref_t<_Rg>> constexpr range_format format_kind<_Rg> = __fmt_kind<_Rg>(); // [format.range.formatter], class template range_formatter template<typename _Tp, typename _CharT = char> requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT> class range_formatter; // TODO /// @cond undocumented namespace __format { // [format.range.fmtdef], class template range-default-formatter template<range_format _Kind, ranges::input_range _Rg, typename _CharT> struct __range_default_formatter; // TODO } // namespace __format /// @endcond // [format.range.fmtmap], [format.range.fmtset], [format.range.fmtstr], // specializations for maps, sets, and strings template<ranges::input_range _Rg, typename _CharT> requires (format_kind<_Rg> != range_format::disabled) && formattable<ranges::range_reference_t<_Rg>, _CharT> struct formatter<_Rg, _CharT> : __format::__range_default_formatter<format_kind<_Rg>, _Rg, _CharT> { }; #endif // C++23 formatting ranges _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // __cpp_lib_format #endif // _GLIBCXX_FORMAT #include <array> #include <string_view> template <auto F> constexpr auto formatted = []{ static constexpr auto array = []{ std::array<char, 100> a = {}; F(a.data()); return a; }(); return std::string_view(array.data()); }(); static_assert(formatted<[](char* p){std::format_to(p, "x={}", 42);}> == "x=42"); static_assert(formatted<[](char* p){std::format_to(p, "x={:{}}", 42, 5);}> == "x= 42");
Become a Patron
Sponsor on GitHub
Donate via PayPal
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
About the author
Statistics
Changelog
Version tree