Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Algol68
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C with Coccinelle
C++ with Coccinelle
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
Mojo
Nim
Numba
Nix
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
PTX
Python
Racket
Raku
Ruby
Rust
Sail
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
Triton
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Yul (Solidity IR)
Zig
Javascript
GIMPLE
Ygen
sway
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 12.5.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 13.4.0
ARM GCC 13.4.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 14.3.0
ARM GCC 14.3.0 (unknown-eabi)
ARM GCC 15.1.0
ARM GCC 15.1.0 (unknown-eabi)
ARM GCC 15.2.0
ARM GCC 15.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 (ex-WINE)
ARM msvc v19.10 (ex-WINE)
ARM msvc v19.14 (ex-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 12.5.0
ARM64 gcc 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 13.3.0
ARM64 gcc 13.4.0
ARM64 gcc 14.1.0
ARM64 gcc 14.2.0
ARM64 gcc 14.3.0
ARM64 gcc 15.1.0
ARM64 gcc 15.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 (ex-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 12.5.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 13.4.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 14.3.0
AVR gcc 15.1.0
AVR gcc 15.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 clang 20.1.0
BPF clang 21.1.0
EDG (experimental reflection)
EDG 6.5
EDG 6.5 (GNU mode gcc 13)
EDG 6.6
EDG 6.6 (GNU mode gcc 13)
EDG 6.7
EDG 6.7 (GNU mode gcc 14)
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
HPPA gcc 14.3.0
HPPA gcc 15.1.0
HPPA gcc 15.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
LoongArch64 clang 20.1.0
LoongArch64 clang 21.1.0
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 13.4.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68K gcc 14.3.0
M68K gcc 15.1.0
M68K gcc 15.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
MinGW gcc 14.3.0
MinGW gcc 15.2.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 12.5.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 13.4.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 14.3.0
RISC-V (32-bits) gcc 15.1.0
RISC-V (32-bits) gcc 15.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 12.5.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 13.4.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 14.3.0
RISC-V (64-bits) gcc 15.1.0
RISC-V (64-bits) gcc 15.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 20.1.0
RISC-V rv32gc clang 21.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 20.1.0
RISC-V rv64gc clang 21.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 12.5.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 13.4.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC LEON gcc 14.3.0
SPARC LEON gcc 15.1.0
SPARC LEON gcc 15.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 12.5.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 13.4.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC gcc 14.3.0
SPARC gcc 15.1.0
SPARC gcc 15.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 12.5.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 13.4.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
SPARC64 gcc 14.3.0
SPARC64 gcc 15.1.0
SPARC64 gcc 15.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 12.5.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 13.4.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI C6x gcc 14.3.0
TI C6x gcc 15.1.0
TI C6x gcc 15.2.0
TI CL430 21.6.1
Tricore gcc 11.3.0 (EEESlab)
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
VAX gcc NetBSDELF 12.4.0 (Apr 16 05:27 2025)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 14.2.0 (20241119)
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 14.2.0 (20241119)
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 14.2.0 (20241119)
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.41 VS17.11
arm64 msvc v19.42 VS17.12
arm64 msvc v19.43 VS17.13
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 20.1.0
armv7-a clang 21.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 20.1.0
armv8-a clang 21.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clad trunk (clang 21.1.0)
clad v1.10 (clang 20.1.0)
clad v1.8 (clang 18.1.0)
clad v1.9 (clang 19.1.0)
clad v2.00 (clang 20.1.0)
clad v2.1 (clang 21.1.0)
clang-cl 18.1.0
ellcc 0.1.33
ellcc 0.1.34
ellcc 2017-07-16
ez80-clang 15.0.0
ez80-clang 15.0.7
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 12.5.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 13.4.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
loongarch64 gcc 14.3.0
loongarch64 gcc 15.1.0
loongarch64 gcc 15.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 clang 20.1.0
mips clang 21.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 12.5.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 13.4.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 14.3.0
mips gcc 15.1.0
mips gcc 15.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 12.5.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 13.4.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 14.3.0
mips64 (el) gcc 15.1.0
mips64 (el) gcc 15.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 clang 20.1.0
mips64 clang 21.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 12.5.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 13.4.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 14.3.0
mips64 gcc 15.1.0
mips64 gcc 15.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
mips64el clang 20.1.0
mips64el clang 21.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 clang 20.1.0
mipsel clang 21.1.0
mipsel gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 12.4.0
mipsel gcc 12.5.0
mipsel gcc 13.1.0
mipsel gcc 13.2.0
mipsel gcc 13.3.0
mipsel gcc 13.4.0
mipsel gcc 14.1.0
mipsel gcc 14.2.0
mipsel gcc 14.3.0
mipsel gcc 15.1.0
mipsel gcc 15.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 12.5.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 13.4.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 14.3.0
power gcc 15.1.0
power gcc 15.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 12.5.0
power64 gcc 13.1.0
power64 gcc 13.2.0
power64 gcc 13.3.0
power64 gcc 13.4.0
power64 gcc 14.1.0
power64 gcc 14.2.0
power64 gcc 14.3.0
power64 gcc 15.1.0
power64 gcc 15.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 12.5.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 13.4.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 14.3.0
power64le gcc 15.1.0
power64le gcc 15.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
qnx 8.0.0
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 12.5.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 13.4.0
s390x gcc 14.1.0
s390x gcc 14.2.0
s390x gcc 14.3.0
s390x gcc 15.1.0
s390x gcc 15.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 12.5.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 13.4.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 14.3.0
sh gcc 15.1.0
sh gcc 15.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (ex-WINE)
x64 msvc v19.10 (ex-WINE)
x64 msvc v19.14 (ex-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.41 VS17.11
x64 msvc v19.42 VS17.12
x64 msvc v19.43 VS17.13
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 (ex-WINE)
x86 msvc v19.10 (ex-WINE)
x86 msvc v19.14 (ex-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.41 VS17.11
x86 msvc v19.42 VS17.12
x86 msvc v19.43 VS17.13
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.11
x86 nvc++ 24.3
x86 nvc++ 24.5
x86 nvc++ 24.7
x86 nvc++ 24.9
x86 nvc++ 25.1
x86 nvc++ 25.3
x86 nvc++ 25.5
x86 nvc++ 25.7
x86 nvc++ 25.9
x86-64 Zapcc 190308
x86-64 clang (-fimplicit-constexpr)
x86-64 clang (Chris Bazley N3089)
x86-64 clang (EricWF contracts)
x86-64 clang (amd-staging)
x86-64 clang (assertions trunk)
x86-64 clang (clangir)
x86-64 clang (experimental -Wlifetime)
x86-64 clang (experimental P1061)
x86-64 clang (experimental P1144)
x86-64 clang (experimental P1221)
x86-64 clang (experimental P2998)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3367)
x86-64 clang (experimental P3372)
x86-64 clang (experimental P3385)
x86-64 clang (experimental P3776)
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 - C++26)
x86-64 clang (reflection - TS)
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 20.1.0
x86-64 clang 20.1.0 (assertions)
x86-64 clang 21.1.0
x86-64 clang 21.1.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 clang rocm-6.2.4
x86-64 clang rocm-6.3.3
x86-64 clang rocm-6.4.0
x86-64 clang rocm-7.0.1
x86-64 gcc (P2034 lambdas)
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.3 (assertions)
x86-64 gcc 10.4
x86-64 gcc 10.4 (assertions)
x86-64 gcc 10.5
x86-64 gcc 10.5 (assertions)
x86-64 gcc 11.1
x86-64 gcc 11.1 (assertions)
x86-64 gcc 11.2
x86-64 gcc 11.2 (assertions)
x86-64 gcc 11.3
x86-64 gcc 11.3 (assertions)
x86-64 gcc 11.4
x86-64 gcc 11.4 (assertions)
x86-64 gcc 12.1
x86-64 gcc 12.1 (assertions)
x86-64 gcc 12.2
x86-64 gcc 12.2 (assertions)
x86-64 gcc 12.3
x86-64 gcc 12.3 (assertions)
x86-64 gcc 12.4
x86-64 gcc 12.4 (assertions)
x86-64 gcc 12.5
x86-64 gcc 12.5 (assertions)
x86-64 gcc 13.1
x86-64 gcc 13.1 (assertions)
x86-64 gcc 13.2
x86-64 gcc 13.2 (assertions)
x86-64 gcc 13.3
x86-64 gcc 13.3 (assertions)
x86-64 gcc 13.4
x86-64 gcc 13.4 (assertions)
x86-64 gcc 14.1
x86-64 gcc 14.1 (assertions)
x86-64 gcc 14.2
x86-64 gcc 14.2 (assertions)
x86-64 gcc 14.3
x86-64 gcc 14.3 (assertions)
x86-64 gcc 15.1
x86-64 gcc 15.1 (assertions)
x86-64 gcc 15.2
x86-64 gcc 15.2 (assertions)
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 2024.2.1
x86-64 icx 2025.0.0
x86-64 icx 2025.0.1
x86-64 icx 2025.0.3
x86-64 icx 2025.0.4
x86-64 icx 2025.1.0
x86-64 icx 2025.1.1
x86-64 icx 2025.2.0
x86-64 icx 2025.2.1
x86-64 icx 2025.2.1
z180-clang 15.0.0
z180-clang 15.0.7
z80-clang 15.0.0
z80-clang 15.0.7
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.14.0
zig c++ 0.14.1
zig c++ 0.15.1
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
#ifndef TUPLET_TUPLET_HPP_IMPLEMENTATION #define TUPLET_TUPLET_HPP_IMPLEMENTATION #include <cstddef> #include <type_traits> #include <utility> /////////////////////////////////////////// //// Tuplet Preprocessor Definitions //// /////////////////////////////////////////// #if TUPLET_NO_INLINE #define TUPLET_INLINE #else #if _MSC_VER #define TUPLET_INLINE __forceinline #elif __GNUC__ || __clang__ #define TUPLET_INLINE [[gnu::always_inline]] #else #define TUPLET_INLINE #endif #endif #define _TUPLET_COMPARISON_OPERATOR_1(type, member, op) \ TUPLET_INLINE constexpr auto operator op(type const& other) \ const noexcept(noexcept(member op other.member)) { \ return member op other.member; \ } #if _MSC_VER /// Looks up a member in a tuple via the base class, and forwards it #define TUPLET_FWD_M(TupleType, BaseType, tup, value) \ static_cast<::tuplet::forward_as_t<TupleType&&, BaseType>>(tup).value /// Gets a member in a tuple via the base class #define TUPLET_GET_M(BaseType, tup, value) \ tup.::tuplet::identity_t<BaseType>::value #elif __clang__ /// Looks up a member in a tuple via the base class, and forwards it #define TUPLET_FWD_M(TupleType, BaseType, tup, value) \ static_cast<TupleType&&>(tup).::tuplet::identity_t<BaseType>::value #define TUPLET_GET_M(BaseType, tup, value) \ tup.::tuplet::identity_t<BaseType>::value #else /// Looks up a member in a tuple via the base class, and forwards it #define TUPLET_FWD_M(TupleType, BaseType, tup, value) \ static_cast<TupleType&&>(tup).BaseType::value #define TUPLET_GET_M(BaseType, tup, value) tup.BaseType::value #endif #if __cpp_impl_three_way_comparison && __cpp_lib_three_way_comparison \ && !defined(TUPLET_DEFAULTED_COMPARISON) #define TUPLET_DEFAULTED_COMPARISON 1 #include <compare> #else #define TUPLET_DEFAULTED_COMPARISON 0 #endif #if __cpp_concepts #define TUPLET_OTHER_THAN(Self, Other) tuplet::other_than<Self> Other #define TUPLET_WEAK_CONCEPT(...) __VA_ARGS__ #define TUPLET_WEAK_REQUIRES(...) requires __VA_ARGS__ #define _TUPLET_TYPES_EQ_WITH(T, U) \ bool \ requires(::tuplet::equality_comparable_with<T, U> && ...) #define _TUPLET_TYPES_CMP_WITH(T, U) \ bool \ requires(::tuplet::equality_comparable_with<T, U> && ...) #else #define TUPLET_OTHER_THAN(Self, Other) \ class Other, class = ::tuplet::sfinae::other_than<Self, Other> #define TUPLET_WEAK_CONCEPT(...) class #define TUPLET_WEAK_REQUIRES(...) #if _MSC_VER < 1930 #define _TUPLET_TYPES_EQ_WITH(T, U) \ ::std::enable_if_t< \ ::tuplet::sfinae::detail::_all_true< \ ::tuplet::sfinae::detail::_has_eq<T, U>...>(), \ bool> #define _TUPLET_TYPES_CMP_WITH(T, U) \ ::std::enable_if_t< \ ::tuplet::sfinae::detail::_all_true< \ ::tuplet::sfinae::detail::_has_cmp<T, U>...>(), \ bool> #else #define _TUPLET_TYPES_EQ_WITH(T, U) \ ::std::enable_if_t<((::tuplet::sfinae::detail::_has_eq<T, U>)&&...), bool> #define _TUPLET_TYPES_CMP_WITH(T, U) \ ::std::enable_if_t<((::tuplet::sfinae::detail::_has_cmp<T, U>)&&...), bool> #endif #endif #if defined(TUPLET_NO_UNIQUE_ADDRESS) && !TUPLET_NO_UNIQUE_ADDRESS #define TUPLET_NO_UNIQUE_ADDRESS #else #if _MSVC_LANG >= 202002L && (!defined __clang__) #define TUPLET_HAS_NO_UNIQUE_ADDRESS 1 #define TUPLET_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] #elif _MSC_VER // no_unique_address is not available (C++17) #define TUPLET_HAS_NO_UNIQUE_ADDRESS 0 #define TUPLET_NO_UNIQUE_ADDRESS #elif __cplusplus > 201703L && (__has_cpp_attribute(no_unique_address)) #define TUPLET_HAS_NO_UNIQUE_ADDRESS 1 #define TUPLET_NO_UNIQUE_ADDRESS [[no_unique_address]] #else // no_unique_address is not available. #define TUPLET_HAS_NO_UNIQUE_ADDRESS 0 #define TUPLET_NO_UNIQUE_ADDRESS #endif #endif //////////////////////////////////////// //// tuplet::type_list Definition //// //////////////////////////////////////// namespace tuplet { /// Represents a list of types template <class... T> struct type_list {}; /// Convinience + operator for catenating type lists template <class... Ls, class... Rs> TUPLET_INLINE constexpr auto operator+(type_list<Ls...>, type_list<Rs...>) { return type_list<Ls..., Rs...> {}; } } // namespace tuplet ///////////////////////////////////////////// //// SFINAE, Concepts, and Type Traits //// ///////////////////////////////////////////// namespace tuplet::sfinae::detail { template < class T, class U, class = decltype(std::declval<T>() == std::declval<U>())> constexpr bool _test_eq(int) { return true; } template <class T, class U> constexpr bool _test_eq(long long) { return false; } template < class T, class U, class = decltype(std::declval<T>() < std::declval<U>())> constexpr bool _test_less(int) { return true; } template <class T, class U> constexpr bool _test_less(long long) { return false; } template <class T, class U> constexpr bool _has_eq = _test_eq<T, U>(0); template <class T, class U = T> constexpr bool _has_cmp = _test_eq<T, U>(0) && _test_less<T, U>(0); template <class Tup, class = typename Tup::base_list> constexpr bool _has_base_list(int) { return true; } template <class Tup> constexpr bool _has_base_list(long long) { return false; } template <bool... B> constexpr bool _all_true() { return (B && ...); }; template <class... T, class... U> constexpr bool _all_has_eq(type_list<T...>, type_list<U...>) { bool values[] {_has_eq<T, U>...}; for (bool b : values) { if (!b) { return false; } } return true; } template <class... T, class... U> constexpr bool _all_has_cmp(type_list<T...>, type_list<U...>) { bool values[] {_has_cmp<T, U>...}; for (bool b : values) { if (!b) { return false; } } return true; } constexpr bool _all_has_eq(type_list<>, type_list<>) { return true; } constexpr bool _all_has_cmp(type_list<>, type_list<>) { return true; } template < class A, class B, class = decltype(std::declval<A>().compare(std::declval<B>()))> constexpr bool _test_m_compare(int) { return true; } template <class, class> constexpr bool _test_m_compare(long long) { return false; } } // namespace tuplet::sfinae::detail namespace tuplet::sfinae { /// Implement assignment but preserve default assignment template <class A, class B> using other_than = std::enable_if_t< !std::is_same_v<std::decay_t<A>, std::decay_t<B>>>; } // namespace tuplet::sfinae namespace tuplet::detail { template <class Tup, class B> struct _forward_as { using type = B&&; }; template <class Tup, class B> struct _forward_as<Tup&, B> { using type = B&; }; template <class Tup, class B> struct _forward_as<Tup&&, B> { using type = B&&; }; template <class Tup, class B> struct _forward_as<Tup const&, B> { using type = B const&; }; template <class Tup, class B> struct _forward_as<Tup const&&, B> { using type = B const&&; }; } // namespace tuplet::detail namespace tuplet { template <class T> struct unwrap_reference { using type = T; }; template <class U> struct unwrap_reference<std::reference_wrapper<U>> { using type = U&; }; template <class T> struct unwrap_ref_decay : unwrap_reference<std::decay_t<T>> {}; template <class T> using unwrap_ref_decay_t = typename unwrap_ref_decay<T>::type; template <class T> using identity_t = T; /// Takes a type B, and gives it the same "rvalue" status as Tup /// Used for forwarding values out of a tuple template <class Tup, class B> using forward_as_t = typename ::tuplet::detail::_forward_as<Tup, B>::type; template <class First, class...> using first_t = First; // Obtains T::type template <class T> using type_t = typename T::type; template <size_t I> using tag = std::integral_constant<size_t, I>; template <size_t I> constexpr tag<I> tag_v {}; template <size_t N> using tag_range = std::make_index_sequence<N>; template <class Tup> using base_list_t = typename std::decay_t<Tup>::base_list; template <class Tup> using element_list_t = typename std::decay_t<Tup>::element_list; template <class T> constexpr bool stateless_v = std::is_empty_v<std::decay_t<T>>; #if __cpp_concepts template <class T, class U> concept same_as = std::is_same_v<T, U> && std::is_same_v<U, T>; template <class T, class U> concept other_than = !std::is_same_v<std::decay_t<T>, std::decay_t<U>>; template <class Tuple> concept base_list_tuple = requires() { typename std::decay_t<Tuple>::base_list; }; template <class T> concept stateless = std::is_empty_v<std::decay_t<T>>; template <class T> concept indexable = stateless<T> || requires(T t) { t[tag<0>()]; }; template <class U, class T> concept assignable_to = requires(U u, T t) { t = u; }; template <class T> concept ordered = requires(T const& t) { { t <=> t }; }; template <class T, class U> concept ordered_with = requires(T const& t, U const& u) { { t <=> u }; }; template <class T> concept equality_comparable = requires(T const& t) { { t == t } -> same_as<bool>; }; template <class T, class U> concept equality_comparable_with = requires(T const& t, U const& u) { { t == u } -> same_as<bool>; }; template <class T> concept partial_comparable = equality_comparable<T> && requires(T const& t) { { t < t } -> same_as<bool>; }; template <class T, class U> concept partial_comparable_with = equality_comparable_with<T, U> && requires(T const& t, U const& u) { { t < u } -> same_as<bool>; { t > u } -> same_as<bool>; }; #endif template <class Tuple> constexpr auto base_list_tuple_v = #if __cpp_concepts base_list_tuple<Tuple>; #else tuplet::sfinae::detail::_has_base_list<Tuple>(0); #endif } // namespace tuplet /////////////////////////////////////////////////////// //// tuplet::detail: Comparison Operator Helpers //// /////////////////////////////////////////////////////// namespace tuplet::detail { /// Computes a partial comparison. Returns true iff a == b. Otherwise, /// sets less to true if a < b template <class T, class U> TUPLET_INLINE constexpr bool _partial_cmp( T const& a, U const& b, bool& less) { if constexpr (::tuplet::sfinae::detail::_test_m_compare<T, U>(0)) { int cmp = a.compare(b); if (cmp < 0) { less = true; } return cmp == 0; } else { #if TUPLET_DEFAULTED_COMPARISON if constexpr (ordered_with<T, U>) { auto cmp = a <=> b; if (cmp < 0) { less = true; } return cmp == 0; } else { if (a < b) { less = true; return false; } else { return a == b; } } #else if (a < b) { less = true; return false; } else { return a == b; } #endif } } template <class Tup, class... B1> TUPLET_INLINE constexpr bool _equals( Tup const& t1, Tup const& t2, type_list<B1...>) { #ifdef _MSC_VER return [&](auto&... v1) -> bool { return [&](auto&... v2) -> bool { return ((v1 == v2) && ...); }(TUPLET_GET_M(B1, t2, value)...); }(TUPLET_GET_M(B1, t1, value)...); #else return ( (TUPLET_GET_M(B1, t1, value) == TUPLET_GET_M(B1, t2, value)) && ...); #endif } template <class Tup, class... B1> TUPLET_INLINE constexpr bool _less( Tup const& t1, Tup const& t2, type_list<B1...>) { bool is_less = false; #ifdef _MSC_VER [&](auto&... v1) -> bool { return [&](auto&... v2) -> bool { return (_partial_cmp(v1, v2, is_less) && ...); }(TUPLET_GET_M(B1, t2, value)...); }(TUPLET_GET_M(B1, t1, value)...); #else (_partial_cmp( TUPLET_GET_M(B1, t1, value), TUPLET_GET_M(B1, t2, value), is_less) && ...); #endif return is_less; } template <class Tup, class... B1> TUPLET_INLINE constexpr bool _less_eq( Tup const& t1, Tup const& t2, type_list<B1...>) { bool is_less = false; #ifdef _MSC_VER bool is_eq = [&](auto&... v1) -> bool { return [&](auto&... v2) -> bool { return (_partial_cmp(v1, v2, is_less) && ...); }(TUPLET_GET_M(B1, t2, value)...); }(TUPLET_GET_M(B1, t1, value)...); #else bool is_eq = (_partial_cmp( TUPLET_GET_M(B1, t1, value), TUPLET_GET_M(B1, t2, value), is_less) && ... && true); #endif return is_less || is_eq; } template <class Tup1, class Tup2, class... B1, class... B2> TUPLET_INLINE constexpr bool _equals( Tup1 const& t1, Tup2 const& t2, type_list<B1...>, type_list<B2...>) { #ifdef _MSC_VER return [&](auto&... v1) -> bool { return [&](auto&... v2) -> bool { return ((v1 == v2) && ...); }(TUPLET_GET_M(B2, t2, value)...); }(TUPLET_GET_M(B1, t1, value)...); #else return ( (TUPLET_GET_M(B1, t1, value) == TUPLET_GET_M(B2, t2, value)) && ...); #endif } template <class Tup1, class Tup2, class... B1, class... B2> TUPLET_INLINE constexpr bool _less( Tup1 const& t1, Tup2 const& t2, type_list<B1...>, type_list<B2...>) { bool is_less = false; #ifdef _MSC_VER [&](auto&... v1) -> bool { return [&](auto&... v2) -> bool { return (_partial_cmp(v1, v2, is_less) && ...); }(TUPLET_GET_M(B2, t2, value)...); }(TUPLET_GET_M(B1, t1, value)...); #else (_partial_cmp( TUPLET_GET_M(B1, t1, value), TUPLET_GET_M(B2, t2, value), is_less) && ... && true); #endif return is_less; } template <class Tup1, class Tup2, class... B1, class... B2> TUPLET_INLINE constexpr bool _less_eq( Tup1 const& t1, Tup2 const& t2, type_list<B1...>, type_list<B2...>) { bool is_less = false; #ifdef _MSC_VER bool is_eq = [&](auto&... v1) -> bool { return [&](auto&... v2) -> bool { return (_partial_cmp(v1, v2, is_less) && ...); }(TUPLET_GET_M(B2, t2, value)...); }(TUPLET_GET_M(B1, t1, value)...); #else bool is_eq = (_partial_cmp( TUPLET_GET_M(B1, t1, value), TUPLET_GET_M(B2, t2, value), is_less) && ... && true); #endif return is_less || is_eq; } } // namespace tuplet::detail //////////////////////////// //// tuplet::type_map //// //////////////////////////// namespace tuplet { template <class... Bases> struct type_map : Bases... { using base_list = type_list<Bases...>; using Bases::operator[]...; using Bases::decl_elem...; #if TUPLET_DEFAULTED_COMPARISON TUPLET_INLINE auto operator<=>(type_map const&) const = default; TUPLET_INLINE bool operator==(type_map const&) const = default; #else TUPLET_INLINE constexpr auto operator==(type_map const& other) const { return detail::_equals(*this, other, base_list {}); } TUPLET_INLINE constexpr auto operator!=(type_map const& other) const { return !(*this == other); } TUPLET_INLINE constexpr auto operator<(type_map const& other) const { return detail::_less(*this, other, base_list {}); } TUPLET_INLINE constexpr auto operator<=(type_map const& other) const { return detail::_less_eq(*this, other, base_list {}); } TUPLET_INLINE constexpr auto operator>(type_map const& other) const { return detail::_less(other, *this, base_list {}); } TUPLET_INLINE constexpr auto operator>=(type_map const& other) const { return detail::_less_eq(other, *this, base_list {}); } #endif }; } // namespace tuplet ////////////////////////////// //// tuplet::tuple_elem //// ////////////////////////////// namespace tuplet { template <size_t I, class T> struct tuple_elem { // Like declval, but with the element static T decl_elem(tag<I>); using type = T; TUPLET_NO_UNIQUE_ADDRESS T value; TUPLET_INLINE constexpr decltype(auto) operator[](tag<I>) & { return (value); } TUPLET_INLINE constexpr decltype(auto) operator[](tag<I>) const& { return (value); } TUPLET_INLINE constexpr decltype(auto) operator[](tag<I>) && { return (static_cast<tuple_elem&&>(*this).value); } #if TUPLET_DEFAULTED_COMPARISON TUPLET_INLINE auto operator<=>(tuple_elem const&) const = default; TUPLET_INLINE bool operator==(tuple_elem const&) const = default; // Implements comparison for tuples containing reference types TUPLET_INLINE constexpr auto operator<=>(tuple_elem const& other) const noexcept(noexcept(value <=> other.value)) requires(std::is_reference_v<T> && ordered<T>) { return value <=> other.value; } TUPLET_INLINE constexpr bool operator==(tuple_elem const& other) const noexcept(noexcept(value == other.value)) requires(std::is_reference_v<T> && equality_comparable<T>) { return value == other.value; } #else _TUPLET_COMPARISON_OPERATOR_1(tuple_elem, value, ==) _TUPLET_COMPARISON_OPERATOR_1(tuple_elem, value, !=) _TUPLET_COMPARISON_OPERATOR_1(tuple_elem, value, <) _TUPLET_COMPARISON_OPERATOR_1(tuple_elem, value, <=) _TUPLET_COMPARISON_OPERATOR_1(tuple_elem, value, >) _TUPLET_COMPARISON_OPERATOR_1(tuple_elem, value, >=) #endif }; } // namespace tuplet /////////////////////////////////////////////////////////////// //// tuplet::tuple_base_t - Base class for tuplet::tuple //// /////////////////////////////////////////////////////////////// namespace tuplet::detail { template <class IndexSequence, class... T> struct _get_tuple_base; template <size_t... I, class... T> struct _get_tuple_base<std::index_sequence<I...>, T...> { using type = type_map<tuple_elem<I, T>...>; }; } // namespace tuplet::detail namespace tuplet { /// Obtains a tuplet::type_map whose bases correspond to /// tuplet::tuple_elem<I, T>. Used as the base class for tuplet::tuple. template <class... T> using tuple_base_t = typename detail:: _get_tuple_base<tag_range<sizeof...(T)>, T...>::type; } // namespace tuplet //////////////////////////////////////////////// //// tuplet::tuple Implementation Details //// //////////////////////////////////////////////// namespace tuplet { template <class... T> struct tuple; } namespace tuplet::detail { template <class Tup, class F, class... B> TUPLET_INLINE constexpr void _for_each( Tup&& tup, F&& func, type_list<B...>) { (void(func(TUPLET_FWD_M(Tup, B, tup, value))), ...); } template <class Tup, class F, class... B> TUPLET_INLINE constexpr bool _any(Tup&& tup, F&& func, type_list<B...>) { #ifdef _MSC_VER return [&](auto&&... v1) -> bool { return (bool(func(static_cast<decltype(v1)&&>(v1))) || ...); }(TUPLET_FWD_M(Tup, B, tup, value)...); #else return (bool(func(TUPLET_FWD_M(Tup, B, tup, value))) || ...); #endif } template <class Tup, class F, class... B> TUPLET_INLINE constexpr bool _all(Tup&& tup, F&& func, type_list<B...>) { #ifdef _MSC_VER return [&](auto&&... v1) -> bool { return (bool(func(static_cast<decltype(v1)&&>(v1))) && ...); }(TUPLET_FWD_M(Tup, B, tup, value)...); #else return (bool(func(TUPLET_FWD_M(Tup, B, tup, value))) && ...); #endif } template <class Tup, class F, class... B> TUPLET_INLINE constexpr auto _map(Tup&& tup, F&& func, type_list<B...>) -> tuple<decltype(func(TUPLET_FWD_M(Tup, B, tup, value)))...> { return {func(TUPLET_FWD_M(Tup, B, tup, value))...}; } template <class Tup, class F, class... B> TUPLET_INLINE constexpr decltype(auto) _apply( Tup&& t, F&& f, type_list<B...>) { return static_cast<F&&>(f)(TUPLET_FWD_M(Tup, B, t, value)...); } template <class U, class Tup, class... B> TUPLET_INLINE constexpr U _convert(Tup&& t, type_list<B...>) { return U {TUPLET_FWD_M(Tup, B, t, value)...}; } } // namespace tuplet::detail //////////////////////////////////////////////// //// tuplet::tuple Primary Implementation //// //////////////////////////////////////////////// namespace tuplet { template <class... T> struct tuple : tuple_base_t<T...> { constexpr static size_t N = sizeof...(T); constexpr static bool #if _MSC_VER nothrow_swappable = ::tuplet::sfinae::detail::_all_true< std::is_nothrow_swappable_v<T>...>(); #else nothrow_swappable = (std::is_nothrow_swappable_v<T> && ...); #endif using super = tuple_base_t<T...>; using super::operator[]; using base_list = typename super::base_list; using element_list = type_list<T...>; using super::decl_elem; template <TUPLET_OTHER_THAN(tuple, U)> // Preserves default assignments TUPLET_INLINE constexpr auto& operator=(U&& tup) { using tuple2 = std::decay_t<U>; if constexpr (base_list_tuple_v<tuple2>) { _assign_tup( static_cast<U&&>(tup), base_list {}, typename tuple2::base_list {}); } else { _assign_index_tup(static_cast<U&&>(tup), tag_range<N>()); } return *this; } template <class... U> TUPLET_WEAK_REQUIRES((assignable_to<U, T> && ...)) constexpr auto& assign(U&&... values) { _assign(base_list {}, static_cast<U&&>(values)...); return *this; } #if TUPLET_DEFAULTED_COMPARISON TUPLET_INLINE auto operator<=>(tuple const&) const = default; TUPLET_INLINE bool operator==(tuple const&) const = default; TUPLET_INLINE bool operator!=(tuple const&) const = default; TUPLET_INLINE bool operator<(tuple const&) const = default; TUPLET_INLINE bool operator>(tuple const&) const = default; TUPLET_INLINE bool operator<=(tuple const&) const = default; TUPLET_INLINE bool operator>=(tuple const&) const = default; #else TUPLET_INLINE constexpr auto operator==(tuple const& other) const { return detail::_equals(*this, other, base_list {}); } TUPLET_INLINE constexpr auto operator!=(tuple const& other) const { return !(*this == other); } TUPLET_INLINE constexpr auto operator<(tuple const& other) const { return detail::_less(*this, other, base_list {}); } TUPLET_INLINE constexpr auto operator<=(tuple const& other) const { return detail::_less_eq(*this, other, base_list {}); } TUPLET_INLINE constexpr auto operator>(tuple const& other) const { return detail::_less(other, *this, base_list {}); } TUPLET_INLINE constexpr auto operator>=(tuple const& other) const { return detail::_less_eq(other, *this, base_list {}); } #endif template <class... U> TUPLET_INLINE constexpr auto operator==(tuple<U...> const& other) const -> _TUPLET_TYPES_EQ_WITH(T, U) { using other_base_list = typename tuple<U...>::base_list; return detail::_equals( *this, other, base_list {}, other_base_list {}); } template <class... U> TUPLET_INLINE constexpr auto operator!=(tuple<U...> const& other) const -> _TUPLET_TYPES_EQ_WITH(T, U) { return !(*this == other); } template <class... U> TUPLET_INLINE constexpr auto operator<(tuple<U...> const& other) const -> _TUPLET_TYPES_CMP_WITH(T, U) { using other_base_list = typename tuple<U...>::base_list; return detail::_less( *this, other, base_list {}, other_base_list {}); } template <class... U> TUPLET_INLINE constexpr auto operator<=(tuple<U...> const& other) const -> _TUPLET_TYPES_CMP_WITH(T, U) { using other_base_list = typename tuple<U...>::base_list; return detail::_less_eq( *this, other, base_list {}, other_base_list {}); } template <class... U> TUPLET_INLINE constexpr auto operator>(tuple<U...> const& other) const -> _TUPLET_TYPES_CMP_WITH(T, U) { using other_base_list = typename tuple<U...>::base_list; return detail::_less( other, *this, other_base_list {}, base_list {}); } template <class... U> TUPLET_INLINE constexpr auto operator>=(tuple<U...> const& other) const -> _TUPLET_TYPES_CMP_WITH(T, U) { using other_base_list = typename tuple<U...>::base_list; return detail::_less_eq( other, *this, other_base_list {}, base_list {}); } TUPLET_INLINE constexpr void swap(tuple& other) noexcept( nothrow_swappable) { _swap(other, base_list {}); } // Applies a function to every element of the tuple. The order is the // declaration order, so first the function will be applied to element // 0, then element 1, then element 2, and so on, where element N is // identified by get<N> template <class F> TUPLET_INLINE constexpr void for_each(F&& func) & { detail::_for_each(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr void for_each(F&& func) const& { detail::_for_each(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr void for_each(F&& func) && { detail::_for_each( static_cast<tuple&&>(*this), static_cast<F&&>(func), base_list {}); } // Applies a function to each element successively, until one returns a // truthy value. Returns true if any application returned a truthy // value, and false otherwise template <class F> TUPLET_INLINE constexpr bool any(F&& func) & { return detail::_any(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr bool any(F&& func) const& { return detail::_any(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr bool any(F&& func) && { return detail::_any( static_cast<tuple&&>(*this), static_cast<F&&>(func), base_list {}); } // Applies a function to each element successively, until one returns a // falsy value. Returns true if every application returned a truthy // value, and false otherwise template <class F> TUPLET_INLINE constexpr bool all(F&& func) & { return detail::_all(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr bool all(F&& func) const& { return detail::_all(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr bool all(F&& func) && { return detail::_all( static_cast<tuple&&>(*this), static_cast<F&&>(func), base_list {}); } // Map a function over every element in the tuple, using the values to // construct a new tuple template <class F> TUPLET_INLINE constexpr auto map(F&& func) & { return detail::_map(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr auto map(F&& func) const& { return detail::_map(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr auto map(F&& func) && { return detail::_map( static_cast<tuple&&>(*this), static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr decltype(auto) apply(F&& func) & { return detail::_apply(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr decltype(auto) apply(F&& func) const& { return detail::_apply(*this, static_cast<F&&>(func), base_list {}); } template <class F> TUPLET_INLINE constexpr decltype(auto) apply(F&& func) && { return detail::_apply( static_cast<tuple&&>(*this), static_cast<F&&>(func), base_list {}); } template <class... U> constexpr explicit operator tuplet::tuple<U...>() & { static_assert( sizeof...(U) == N, "Can only convert to tuples with the same number of items"); return detail::_convert<tuplet::tuple<U...>>(*this, base_list {}); } template <class... U> constexpr explicit operator tuplet::tuple<U...>() const& { static_assert( sizeof...(U) == N, "Can only convert to tuples with the same number of items"); return detail::_convert<tuplet::tuple<U...>>(*this, base_list {}); } template <class... U> constexpr explicit operator tuplet::tuple<U...>() && { static_assert( sizeof...(U) == N, "Can only convert to tuples with the same number of items"); return detail::_convert<tuplet::tuple<U...>>( static_cast<tuple&&>(*this), base_list {}); } /// Instantiate the given type using list initialization template <class U> TUPLET_INLINE constexpr U as() & { return detail::_convert<U>(*this, base_list {}); } /// Instantiate the given type using list initialization template <class U> TUPLET_INLINE constexpr U as() const& { return detail::_convert<U>(*this, base_list {}); } /// Instantiate the given type using list initialization template <class U> TUPLET_INLINE constexpr U as() && { return detail::_convert<U>( static_cast<tuple&&>(*this), base_list {}); } private: template <class... B> TUPLET_INLINE constexpr void _swap( tuple& other, type_list<B...>) noexcept(nothrow_swappable) { using std::swap; (swap(B::value, TUPLET_GET_M(B, other, value)), ...); } template <class U, class... B1, class... B2> TUPLET_INLINE constexpr void _assign_tup( U&& u, type_list<B1...>, type_list<B2...>) { // See: // https://developercommunity.visualstudio.com/t/fold-expressions-unreliable-in-171-with-c20/1676476 (void(B1::value = TUPLET_FWD_M(U, B2, u, value)), ...); } template <class U, size_t... I> TUPLET_INLINE constexpr void _assign_index_tup( U&& u, std::index_sequence<I...>) { using std::get; (void(tuple_elem<I, T>::value = get<I>(static_cast<U&&>(u))), ...); } template <class... U, class... B> TUPLET_INLINE constexpr void _assign(type_list<B...>, U&&... u) { (void(B::value = static_cast<U&&>(u)), ...); } }; } // namespace tuplet //////////////////////////////////////////////////// //// tuplet::tuple Empty Tuple Specialization //// //////////////////////////////////////////////////// namespace tuplet { template <> struct tuple<> : tuple_base_t<> { constexpr static size_t N = 0; constexpr static bool nothrow_swappable = true; using super = tuple_base_t<>; using base_list = type_list<>; using element_list = type_list<>; template <TUPLET_OTHER_THAN(tuple, U)> // Preserves default assignments TUPLET_WEAK_REQUIRES( stateless<U>) // Check that U is similarly stateless constexpr auto& operator=(U&&) noexcept { return *this; } constexpr void swap(tuple) noexcept {} constexpr auto& assign() noexcept { return *this; } #if TUPLET_DEFAULTED_COMPARISON TUPLET_INLINE auto operator<=>(tuple const&) const = default; TUPLET_INLINE bool operator==(tuple const&) const = default; #else TUPLET_INLINE constexpr bool operator==(tuple const&) const noexcept { return true; } TUPLET_INLINE constexpr bool operator<=(tuple const&) const noexcept { return true; } TUPLET_INLINE constexpr bool operator>=(tuple const&) const noexcept { return true; } TUPLET_INLINE constexpr bool operator!=(tuple const&) const noexcept { return false; } TUPLET_INLINE constexpr bool operator<(tuple const&) const noexcept { return false; } TUPLET_INLINE constexpr bool operator>(tuple const&) const noexcept { return false; } #endif // Applies a function to every element of the tuple. The order is the // declaration order, so first the function will be applied to element // 0, then element 1, then element 2, and so on, where element N is // identified by get<N> // // (Does nothing when invoked on empty tuple) template <class F> constexpr void for_each(F&&) const noexcept {} // Applies a function to each element successively, until one returns a // truthy value. Returns true if any application returned a truthy // value, and false otherwise // // (Returns false for empty tuple) template <class F> constexpr bool any(F&&) const noexcept { return false; } // Applies a function to each element successively, until one returns a // falsy value. Returns true if every application returned a truthy // value, and false otherwise // // (Returns true for empty tuple) template <class F> constexpr bool all(F&&) const noexcept { return true; } // Map a function over every element in the tuple, using the values to // construct a new tuple // // (Returns empty tuple when invoked on empty tuple) template <class F> constexpr auto map(F&&) const noexcept { return tuple {}; } template <class F> constexpr decltype(auto) apply(F&& func) const noexcept { return func(); } template <class U> constexpr U as() const noexcept { return U {}; } }; template <class... Ts> tuple(Ts...) -> tuple<unwrap_ref_decay_t<Ts>...>; } // namespace tuplet /////////////////////////////////////// //// tuplet::pair Implementation //// /////////////////////////////////////// namespace tuplet { template <class First, class Second> struct pair { constexpr static size_t N = 2; constexpr static bool nothrow_swappable = std::is_nothrow_swappable_v<First> && std::is_nothrow_swappable_v<Second>; TUPLET_NO_UNIQUE_ADDRESS First first; TUPLET_NO_UNIQUE_ADDRESS Second second; TUPLET_INLINE constexpr decltype(auto) operator[](tag<0>) & { return (first); } TUPLET_INLINE constexpr decltype(auto) operator[](tag<0>) const& { return (first); } TUPLET_INLINE constexpr decltype(auto) operator[](tag<0>) && { return (static_cast<pair&&>(*this).first); } TUPLET_INLINE constexpr decltype(auto) operator[](tag<1>) & { return (second); } TUPLET_INLINE constexpr decltype(auto) operator[](tag<1>) const& { return (second); } TUPLET_INLINE constexpr decltype(auto) operator[](tag<1>) && { return (static_cast<pair&&>(*this).second); } TUPLET_INLINE void swap(pair& other) noexcept(nothrow_swappable) { using std::swap; swap(first, other.first); swap(second, other.second); } template < TUPLET_OTHER_THAN(pair, Type)> // Preserves default assignments TUPLET_INLINE constexpr auto& operator=(Type&& tup) { auto&& [a, b] = static_cast<Type&&>(tup); first = static_cast<decltype(a)&&>(a); second = static_cast<decltype(b)&&>(b); return *this; } template < TUPLET_WEAK_CONCEPT(assignable_to<First>) F2, TUPLET_WEAK_CONCEPT(assignable_to<Second>) S2> TUPLET_INLINE constexpr auto& assign(F2&& f, S2&& s) { first = static_cast<F2&&>(f); second = static_cast<S2&&>(s); return *this; } #if TUPLET_DEFAULTED_COMPARISON auto operator<=>(pair const&) const = default; bool operator==(pair const&) const = default; #else TUPLET_INLINE constexpr bool operator==(pair const& other) const { return first == other.first && second == other.second; } TUPLET_INLINE constexpr bool operator!=(pair const& other) const { return !(*this == other); } TUPLET_INLINE constexpr bool operator<(pair const& other) const { bool result = false; detail::_partial_cmp(first, other.first, result) && detail::_partial_cmp(second, other.second, result); return result; } TUPLET_INLINE constexpr bool operator<=(pair const& other) const { bool result = false; bool is_eq = detail::_partial_cmp(first, other.first, result) && detail::_partial_cmp(second, other.second, result); return result || is_eq; } TUPLET_INLINE constexpr bool operator>(pair const& other) const { bool result = false; detail::_partial_cmp(other.first, first, result) && detail::_partial_cmp(other.second, second, result); return result; } TUPLET_INLINE constexpr bool operator>=(pair const& other) const { bool result = false; bool is_eq = detail::_partial_cmp(other.first, first, result) && detail::_partial_cmp(other.second, second, result); return result || is_eq; } #endif }; template <class A, class B> pair(A, B) -> pair<unwrap_ref_decay_t<A>, unwrap_ref_decay_t<B>>; } // namespace tuplet ////////////////////////////////////////////////////// //// tuplet::convert: tuple conversion facility //// ////////////////////////////////////////////////////// namespace tuplet { // Converts from one tuple type to any other tuple or U template <class Tuple> struct convert { using base_list = typename std::decay_t<Tuple>::base_list; Tuple tuple; template <class U> constexpr operator U() && { return detail::_convert<U>( static_cast<Tuple&&>(tuple), base_list {}); } }; template <class Tuple> convert(Tuple&) -> convert<Tuple&>; template <class Tuple> convert(Tuple const&) -> convert<Tuple const&>; template <class Tuple> convert(Tuple&&) -> convert<Tuple>; } // namespace tuplet ///////////////////////////////////////////////////////// //// tuplet Appendix 1: Small non-member functions //// ///////////////////////////////////////////////////////// // tuplet::get implementation // tuplet::tie implementation // tuplet::apply implementation // tuplet::swap // tuplet::make_tuple // tuplet::forward_as_tuple namespace tuplet { template <size_t I, TUPLET_WEAK_CONCEPT(indexable) Tup> TUPLET_INLINE constexpr decltype(auto) get(Tup&& tup) { return static_cast<Tup&&>(tup)[tag<I>()]; } template <class... T> TUPLET_INLINE constexpr tuple<T&...> tie(T&... t) { return {t...}; } template <class F, TUPLET_WEAK_CONCEPT(base_list_tuple) Tup> TUPLET_INLINE constexpr decltype(auto) apply(F&& func, Tup&& tup) { return detail::_apply( static_cast<Tup&&>(tup), static_cast<F&&>(func), typename std::decay_t<Tup>::base_list {}); } template <class F, class A, class B> TUPLET_INLINE constexpr decltype(auto) apply( F&& func, tuplet::pair<A, B>& pair) { return static_cast<F&&>(func)(pair.first, pair.second); } template <class F, class A, class B> TUPLET_INLINE constexpr decltype(auto) apply( F&& func, tuplet::pair<A, B> const& pair) { return static_cast<F&&>(func)(pair.first, pair.second); } template <class F, class A, class B> TUPLET_INLINE constexpr decltype(auto) apply( F&& func, tuplet::pair<A, B>&& pair) { using P = tuplet::pair<A, B>&&; return static_cast<F&&>( func)(static_cast<P>(pair).first, static_cast<P>(pair).second); } template <class... T> TUPLET_INLINE void swap(tuple<T...>& a, tuple<T...>& b) noexcept( tuple<T...>::nothrow_swappable) { a.swap(b); } template <class A, class B> TUPLET_INLINE void swap(pair<A, B>& a, pair<A, B>& b) noexcept( pair<A, B>::nothrow_swappable) { a.swap(b); } template <typename... Ts> TUPLET_INLINE constexpr auto make_tuple(Ts&&... args) { return tuple<unwrap_ref_decay_t<Ts>...> {static_cast<Ts&&>(args)...}; } template <typename... T> TUPLET_INLINE constexpr auto forward_as_tuple(T&&... a) noexcept { return tuple<T&&...> {static_cast<T&&>(a)...}; } } // namespace tuplet /////////////////////////////////////////////////////////// //// tuplet Appendix 2: The Horror that is tuple_cat //// /////////////////////////////////////////////////////////// namespace tuplet::detail { template <class T, class... Q> TUPLET_INLINE constexpr auto _repeat_type(type_list<Q...>) { return type_list<first_t<T, Q>...> {}; } template <class... Outer> TUPLET_INLINE constexpr auto _get_outer_bases(type_list<Outer...>) { return (_repeat_type<Outer>(base_list_t<type_t<Outer>> {}) + ...); } template <class... Outer> TUPLET_INLINE constexpr auto _get_inner_bases(type_list<Outer...>) { return (base_list_t<type_t<Outer>> {} + ...); } // This takes a forwarding tuple as a parameter. The forwarding tuple only // contains references, so it should just be taken by value. template <class T, class... Outer, class... Inner> TUPLET_INLINE constexpr auto _tuple_cat( T tup, type_list<Outer...>, type_list<Inner...>) -> tuple<type_t<Inner>...> { return {TUPLET_FWD_M( type_t<Outer>, Inner, TUPLET_GET_M(Outer, tup, value), value)...}; } } // namespace tuplet::detail namespace tuplet { template <TUPLET_WEAK_CONCEPT(base_list_tuple)... T> constexpr auto tuple_cat(T&&... ts) { if constexpr (sizeof...(T) == 0) { return tuple<>(); } else { /** * It appears that Clang produces better assembly when * TUPLET_CAT_BY_FORWARDING_TUPLE == 0, while GCC produces better assembly when * TUPLET_CAT_BY_FORWARDING_TUPLE == 1. MSVC always produces terrible assembly * in either case. This will set TUPLET_CAT_BY_FORWARDING_TUPLE to the correct * value (0 for clang, 1 for everyone else) * * See: https://github.com/codeinred/tuplet/discussions/14 */ #if !defined(TUPLET_CAT_BY_FORWARDING_TUPLE) #if defined(__clang__) #define TUPLET_CAT_BY_FORWARDING_TUPLE 0 #else #define TUPLET_CAT_BY_FORWARDING_TUPLE 1 #endif #endif #if TUPLET_CAT_BY_FORWARDING_TUPLE using big_tuple = tuple<T&&...>; #else using big_tuple = tuple<std::decay_t<T>...>; #endif using outer_bases = base_list_t<big_tuple>; constexpr auto outer = detail::_get_outer_bases(outer_bases {}); constexpr auto inner = detail::_get_inner_bases(outer_bases {}); return detail::_tuple_cat( big_tuple {static_cast<T&&>(ts)...}, outer, inner); } } } // namespace tuplet /////////////////////////////////////////// //// tuplet Appendix 3: tag literals //// /////////////////////////////////////////// namespace tuplet::literals::detail { template <char... D> constexpr size_t _size_t_from_digits() { static_assert( (('0' <= D && D <= '9') && ...), "Must be integral literal"); size_t num = 0; return ((num = num * 10 + (D - '0')), ..., num); } } // namespace tuplet::literals::detail namespace tuplet::literals { template <char... D> constexpr auto operator""_tag() noexcept -> tag<detail::_size_t_from_digits<D...>()> { return {}; } } // namespace tuplet::literals ////////////////////////////////////////////////////////////////// //// tuplet Appendix 4: std::tuple_size, std::tuple_element //// ////////////////////////////////////////////////////////////////// namespace std { template <class... T> struct tuple_size<tuplet::tuple<T...>> : std::integral_constant<size_t, sizeof...(T)> {}; template <size_t I, class... T> struct tuple_element<I, tuplet::tuple<T...>> { using type = decltype(tuplet::tuple<T...>::decl_elem(tuplet::tag<I>())); }; template <class A, class B> struct tuple_size<tuplet::pair<A, B>> : std::integral_constant<size_t, 2> {}; template <size_t I, class A, class B> struct tuple_element<I, tuplet::pair<A, B>> { static_assert(I < 2, "tuplet::pair only has 2 elements"); using type = std::conditional_t<I == 0, A, B>; }; } // namespace std #endif int main() { auto t = tuplet::make_tuple(1, 2, 3); return 0; }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Compiler Explorer Shop
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
Statistics
Changelog
Version tree