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#
Go
Haskell
HLSL
Hook
Hylo
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
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
Zig
Javascript
GIMPLE
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 13.1.0
ARM GCC 13.2.0
ARM GCC 13.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 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 5.4
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 13.1.0
AVR gcc 13.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 gcc 13.1.0
BPF gcc 13.2.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
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)
M68K gcc 13.1.0
M68K gcc 13.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 13.1.0
RISC-V (32-bits) gcc 13.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 13.1.0
RISC-V (64-bits) gcc 13.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 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 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 13.1.0
SPARC LEON gcc 13.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 13.1.0
TI C6x gcc 13.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.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30
arm64 msvc v19.31
arm64 msvc v19.32
arm64 msvc v19.33
arm64 msvc v19.34
arm64 msvc v19.35
arm64 msvc v19.36
arm64 msvc v19.37
arm64 msvc v19.38
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 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 9.0.0
armv8-a clang 9.0.1
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 13.1.0
loongarch64 gcc 13.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 gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 13.1.0
mips gcc 13.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 13.1.0
mips64 (el) gcc 13.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 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 13.1.0
mips64 gcc 13.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
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 gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 13.1.0
mipsel gcc 13.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 13.1.0
power gcc 13.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 13.1.0
power64 gcc 13.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 13.1.0
power64le gcc 13.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 13.1.0
s390x gcc 13.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 13.1.0
sh gcc 13.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
x64 msvc v19.14 (WINE)
x64 msvc v19.15
x64 msvc v19.16
x64 msvc v19.20
x64 msvc v19.21
x64 msvc v19.22
x64 msvc v19.23
x64 msvc v19.24
x64 msvc v19.25
x64 msvc v19.26
x64 msvc v19.27
x64 msvc v19.28
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30
x64 msvc v19.31
x64 msvc v19.32
x64 msvc v19.33
x64 msvc v19.34
x64 msvc v19.35
x64 msvc v19.36
x64 msvc v19.37
x64 msvc v19.38
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
x86 msvc v19.14 (WINE)
x86 msvc v19.15
x86 msvc v19.16
x86 msvc v19.20
x86 msvc v19.21
x86 msvc v19.22
x86 msvc v19.23
x86 msvc v19.24
x86 msvc v19.25
x86 msvc v19.26
x86 msvc v19.27
x86 msvc v19.28
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30
x86 msvc v19.31
x86 msvc v19.32
x86 msvc v19.33
x86 msvc v19.34
x86 msvc v19.35
x86 msvc v19.36
x86 msvc v19.37
x86 msvc v19.38
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-64 Zapcc 190308
x86-64 clang (amd-stg-open)
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 P2996)
x86-64 clang (experimental metaprogramming - P2632)
x86-64 clang (experimental pattern matching)
x86-64 clang (old concepts branch)
x86-64 clang (reflection)
x86-64 clang (resugar)
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 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 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 13.1
x86-64 gcc 13.2
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 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 (latest)
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
zig c++ 0.10.0
zig c++ 0.11.0
zig c++ 0.12.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
#include <cstddef> #include <tuple> #include <type_traits> // inspired by https://github.com/kennytm/utils/blob/master/traits.hpp // and https://stackoverflow.com/a/28213747. Further thanks to G. Sliepen // on StackExchange CodeReview for numerous helpful suggestions. // // invocable_traits inspects a callable and allows to query information about // its return type, host class (if member function or data member, this // includes lambdas), arity, argument types, and several properties of the // function declaration. Specifically, the following properties are exposed: // arity : std::size_t value indicating the arity of the callable // (not counting variadic input arguments, so int(int,...) // has arity 1) // is_const : true if the callable is declared const // is_volatile : true if the callable is declared volatile // is_noexcept : true if the callable is declared noexcept // is_variadic : true if the callable has an old-/C-style variadic // argument // declared_result_t: return type as declared in the callable signature // invoke_result_t : return type when the callable is std::invoke()d // class_t : class this callable is a member of, void if free // function // arg_t : indexable list of the declared argument types of // the callable (e.g. use arg_t<0> to retrieve the // first argument type) // error : if an error occurs (e.g. provided type is not callable) // that is communicated through this property. You can // issue an appropriate static_assert using the following: // // example that will fail because int is not callable: // using traits = invocable_traits::get<int>; // invocable_traits::issue_error<traits::error>(); // Upon error, all other properties are set to void or // false, as appropriate. // // When the user has provided argument types and they are used (see below), // three additional properties are exposed: // num_matched_overloads : number of overloads found that matched the // user's provided argument types. // is_exact_match : true if the user-provided argument types // were an exact match to an overload, false // if the overload was found through the search // procedure (see 2 below). // matched_overload : indexable list of matched overloads, each // containing all the info about the matched // callable, as described above. Use, e.g. // matched_overload<0>::arg_t<0> to query the // declared type of the first argument of the // first found overload. // // Note that this machinery does not handle overloaded or templated functions // (but see below for the special case of overloaded operator()). It could // not possibly do so, since these do not have a unique address. If references // or pointers to specific overloads or template instantiations are passed, // all works as expected. // // To handle overloaded or templated operator() of functors (this includes // lambdas), you can help invocable_traits find the right overload by // providing additional type arguments, e.g., // invocable_traits::get<Functor, int>. If required for disambiguation (i.e. // if the passed callable is indeed a functor with an overloaded or templated // operator()), these extra type arguments are used to resolve the desired // overload. If the additional type arguments are not required to resolve the // overload, or a callable other than a functor is passed, the additional // type arguments are ignored. It is thus not an error to provide the // additional type arguments (e.g. if you know what input arguments your // callable should take), but the callable will not be checked for a matching // signature unless the additional type arguments were required for overload // resolution/template instantiation. Use of std::is_invocable<> is therefore // adviced in all cases where you provide additional type arguments to // invocable_traits::get<>. // // When resolving the overload using these extra type arguments, two things // may happen: // 1. the user provides the exact argument types (including const and // reference qualifiers) of an existing overload. In that case, // invocable_traits for only that overload are returned. // 2. the user provides argument types that do not exactly match an // existing overload. In that case, invocable_traits generates all // possible const and reference qualified versions of the provided // argument types, and tests for overloads with all their permutations. // If any are found, invocable_traits for the first found overload are // returned, and invocable_traits for any additional matching overloads // can also be retrieved (see propeties above). // When both procedures fail to yield a matching overload, an // invocable_traits::error is set. // // Note that even though std::is_invocable<> may yield true, there are // various situations where invocable_traits::get<> will fail to find the // right overload when provided with the same type arguments as // std::is_invocable<>. These include at least: // - implicit conversions of the argument type // - old-/C-style variadic functions // - default arguments // To be able to find an overload, the correct type (excluding qualifiers, // see procedure 2 above) of all input arguments (also default arguments) // must be specified, and variadic inputs should not be specified (the // presence of these will be deduced by invocable_traits and signalled // through invocable_traits::is_variadic = true). namespace invocable_traits { enum class Error { None, NotAClass, NoCallOperator, IsOverloadedTemplated, OverloadNotResolved, Unknown }; template <Error E> void issue_error() { static_assert(E != Error::NotAClass, "passed type is not a class, and thus cannot have an operator()"); static_assert(E != Error::NoCallOperator, "passed type is a class that doesn't have an operator()"); static_assert(E != Error::IsOverloadedTemplated, "passed passed type is a class that has an overloaded or templated operator(), specify argument types in invocable_traits invocation to disambiguate the operator() signature you wish to use"); static_assert(E != Error::OverloadNotResolved, "passed type is a class that doesn't have an operator() that declares the specified argument types, or some const/ref-qualified combination of the specified argument types"); static_assert(E != Error::Unknown, "an unknown error occurred"); }; namespace detail { template <bool, std::size_t i, typename... Args> struct invocable_traits_arg_impl { using type = std::tuple_element_t<i, std::tuple<Args...>>; }; template <std::size_t i, typename... Args> struct invocable_traits_arg_impl<false, i, Args...> { static_assert(i < sizeof...(Args), "Argument index out of bounds (queried callable does not declare this many arguments)"); // to reduce excessive compiler error output using type = void; }; template < typename Rd, typename Ri, typename C, bool IsConst, bool isVolatile, bool isNoexcept, bool IsVariadic, typename... Args> struct invocable_traits_class { static constexpr std::size_t arity = sizeof...(Args); static constexpr auto is_const = IsConst; static constexpr auto is_volatile = isVolatile; static constexpr auto is_noexcept = isNoexcept; static constexpr auto is_variadic = IsVariadic; using declared_result_t = Rd; // return type as declared in function using invoke_result_t = Ri; // return type of std::invoke() expression using class_t = C; template <std::size_t i> using arg_t = typename invocable_traits_arg_impl<i < sizeof...(Args), i, Args...>::type; static constexpr Error error = Error::None; }; template < typename Rd, typename Ri, bool IsConst, bool isVolatile, bool isNoexcept, bool IsVariadic, typename... Args> struct invocable_traits_free : public invocable_traits_class<Rd, Ri, void, IsConst, isVolatile, isNoexcept, IsVariadic, Args...> {}; // machinery to extract exact function signature and qualifications template <typename, typename...> struct invocable_traits_impl; // pointers to data members template <typename C, typename R> struct invocable_traits_impl<R C::*> : public invocable_traits_class<R, std::invoke_result_t<R C::*, C>, C, false, false, false, false > {}; // pointers to functions template <typename R, typename... Args> struct invocable_traits_impl<R(*)(Args...)> : public invocable_traits_impl<R(Args...)> {}; template <typename R, typename... Args> struct invocable_traits_impl<R(*)(Args...) noexcept> : public invocable_traits_impl<R(Args...) noexcept> {}; template <typename R, typename... Args> struct invocable_traits_impl<R(*)(Args..., ...)> : public invocable_traits_impl<R(Args..., ...)> {}; template <typename R, typename... Args> struct invocable_traits_impl<R(*)(Args..., ...) noexcept> : public invocable_traits_impl<R(Args..., ...) noexcept> {}; template <typename...> struct typelist {}; # define IS_NONEMPTY(...) 0 __VA_OPT__(+1) # define MAKE_CONST(...) __VA_OPT__(const) # define MAKE_VOLATILE(...) __VA_OPT__(volatile) # define MAKE_NOEXCEPT(...) __VA_OPT__(noexcept) # define MAKE_VARIADIC(...) __VA_OPT__(, ...) // functions, pointers to member functions and machinery to select a specific overloaded operator() # define INVOCABLE_TRAITS_SPEC(c,vo,e,va) \ /* functions */ \ template <typename R, typename... Args> \ struct invocable_traits_impl<R(Args... MAKE_VARIADIC(va)) \ MAKE_CONST(c) MAKE_VOLATILE(vo) MAKE_NOEXCEPT(e)> \ : public invocable_traits_free< \ R, \ std::invoke_result_t<R(Args... MAKE_VARIADIC(va)) \ MAKE_CONST(c) MAKE_VOLATILE(vo) MAKE_NOEXCEPT(e), Args...>, \ IS_NONEMPTY(c), \ IS_NONEMPTY(vo), \ IS_NONEMPTY(e), \ IS_NONEMPTY(va), \ Args...> {}; \ /* pointers to member functions */ \ template <typename C, typename R, typename... Args> \ struct invocable_traits_impl<R(C::*)(Args... MAKE_VARIADIC(va)) \ MAKE_CONST(c) MAKE_VOLATILE(vo) MAKE_NOEXCEPT(e)> \ : public invocable_traits_class< \ R, \ std::invoke_result_t<R(C::*)(Args...MAKE_VARIADIC(va)) \ MAKE_CONST(c) MAKE_VOLATILE(vo) MAKE_NOEXCEPT(e), C, Args...>, \ C, \ IS_NONEMPTY(c), \ IS_NONEMPTY(vo), \ IS_NONEMPTY(e), \ IS_NONEMPTY(va), \ Args...> {}; \ /* machinery to select a specific overloaded operator() */ \ template <typename C, typename... OverloadArgs> \ auto invocable_traits_resolve_overload(std::invoke_result_t<C, OverloadArgs...> \ (C::*func)(OverloadArgs... MAKE_VARIADIC(va)) \ MAKE_CONST(c) MAKE_VOLATILE(vo) MAKE_NOEXCEPT(e),\ typelist<OverloadArgs...>)\ { return func; }; // cover all const, volatile and noexcept permutations INVOCABLE_TRAITS_SPEC( , , , ) INVOCABLE_TRAITS_SPEC( ,Va, , ) INVOCABLE_TRAITS_SPEC( , ,E, ) INVOCABLE_TRAITS_SPEC( ,Va,E, ) INVOCABLE_TRAITS_SPEC( , , ,Vo) INVOCABLE_TRAITS_SPEC( ,Va, ,Vo) INVOCABLE_TRAITS_SPEC( , ,E,Vo) INVOCABLE_TRAITS_SPEC( ,Va,E,Vo) INVOCABLE_TRAITS_SPEC(C, , , ) INVOCABLE_TRAITS_SPEC(C,Va, , ) INVOCABLE_TRAITS_SPEC(C, ,E, ) INVOCABLE_TRAITS_SPEC(C,Va,E, ) INVOCABLE_TRAITS_SPEC(C, , ,Vo) INVOCABLE_TRAITS_SPEC(C,Va, ,Vo) INVOCABLE_TRAITS_SPEC(C, ,E,Vo) INVOCABLE_TRAITS_SPEC(C,Va,E,Vo) // clean up # undef INVOCABLE_TRAITS_SPEC # undef IS_NONEMPTY # undef MAKE_CONST # undef MAKE_VOLATILE # undef MAKE_NOEXCEPT # undef MAKE_VARIADIC // check if passed type has an operator(), can be true for struct/class, includes lambdas // from https://stackoverflow.com/a/70699109/3103767 // logic: test if &Tester<C>::operator() is a valid expression. It is only valid if // C did not have an operator(), because else &Tester<C>::operator() would be ambiguous // and thus invalid. To test if C has an operator(), we just check that // &Tester<C>::operator() fails, which implies that C has an operator() declared. // This trick does not work for final classes, at least detect non-overloaded // operator() for those. struct Fake { void operator()(); }; template <typename T> struct Tester : T, Fake { }; template <typename C> concept HasCallOperator = std::is_class_v<C> and ( requires(C) // checks if non-overloaded operator() exists { &C::operator(); } or not requires(Tester<C>) // checks overloaded/templated operator(), but doesn't work on final classes { &Tester<C>::operator(); } ); // check if we can get operator(). // If it fails (and assuming above HasCallOperator does pass), // this is because the operator is overloaded or templated // NB: can't simply do requires(C t){ &C::operator(); } because // that is too lenient on clang, also succeeds for // overloaded/templated operator() template <typename T> concept CanGetCallOperator = requires { invocable_traits_impl< decltype( &T::operator() )>(); }; // check if we can get an operator() that takes the specified arguments types. // If it fails (and assuming above HasCallOperator does pass), // an operator() with this specified cv- and ref-qualified argument types does // not exist. template <typename C, typename... OverloadArgs> concept HasSpecificCallOperator = requires { invocable_traits_resolve_overload<C>( &C::operator(), typelist<OverloadArgs...>{} ); }; namespace try_harder { // cartesian product of type lists, from https://stackoverflow.com/a/59422700/3103767 template <typename... Ts> typelist<typelist<Ts>...> layered(typelist<Ts...>); template <typename... Ts, typename... Us> auto operator+(typelist<Ts...>, typelist<Us...>) ->typelist<Ts..., Us...>; template <typename T, typename... Us> auto operator*(typelist<T>, typelist<Us...>) ->typelist<decltype(T{} + Us{})...>; template <typename... Ts, typename TL> auto operator^(typelist<Ts...>, TL tl) -> decltype(((typelist<Ts>{} *tl) + ...)); template <typename... TLs> using product_t = decltype((layered(TLs{}) ^ ...)); // adapter to make cartesian product of a list of typelists template <typename... Ts> auto list_product(typelist<Ts...>) ->product_t<Ts...>; template <typename T> using list_product_t = decltype(list_product(T{})); // code to turn input argument type T into all possible const/ref-qualified versions template <typename T> struct type_maker_impl; // T* -> T*, T* const template <typename T> requires std::is_pointer_v<T> struct type_maker_impl<T> { using type = typelist<T, const T>; }; // T -> T, T&, const T&, T&&, const T&& (NB: const on const T is ignored, so that type is not included) template <typename T> requires (!std::is_pointer_v<T>) struct type_maker_impl<T> { using type = typelist<T, T&, const T&, T&&, const T&&>; }; template <typename T> struct type_maker : type_maker_impl<std::remove_cvref_t<T>> {}; template <typename T> using type_maker_t = typename type_maker<T>::type; template <typename ...Ts> struct type_maker_for_typelist { using type = typelist<type_maker_t<Ts>...>; }; // code to filter out combinations of qualified input arguments that do not // resolve to a declared overload template <typename, typename> struct concat; template <typename T, typename ...Args> struct concat<T, typelist<Args...>> { using type = typelist<T, Args...>; }; template <typename...> struct check; template <typename C, typename... Args> struct check<C, typelist<Args...>> { static constexpr bool value = HasSpecificCallOperator<C, Args...>; }; template <typename...> struct filter; template <typename C> struct filter<C, typelist<>> { using type = typelist<>; }; template <typename C, typename Head, typename ...Tail> struct filter<C, typelist<Head, Tail...>> { using type = std::conditional_t<check<C, Head>::value, typename concat<Head, typename filter<C, typelist<Tail...>>::type>::type, typename filter<C, typelist<Tail...>>::type >; }; // extract first element from a typelist template <typename, typename...> struct get_head; template <typename Head, typename ...Tail> struct get_head<typelist<Head, Tail...>> { using type = Head; }; } // to reduce excessive compiler error output struct invocable_traits_error { static constexpr std::size_t arity = 0; static constexpr auto is_const = false; static constexpr auto is_volatile = false; static constexpr auto is_noexcept = false; static constexpr auto is_variadic = false; using declared_result_t = void; using invoke_result_t = void; using class_t = void; template <size_t i> using arg_t = void; }; struct invocable_traits_error_overload { static constexpr std::size_t num_matched_overloads = 0; static constexpr auto is_exact_match = false; template <size_t i> using matched_overload = invocable_traits_error; }; template <typename T, bool hasOverloadArgs> constexpr Error get_error() { if constexpr (!std::is_class_v<T>) return Error::NotAClass; else if constexpr (!HasCallOperator<T>) return Error::NoCallOperator; else if constexpr (hasOverloadArgs) return Error::OverloadNotResolved; else if constexpr (!hasOverloadArgs && !CanGetCallOperator<T>) return Error::IsOverloadedTemplated; return Error::Unknown; } template <typename C, typename Head> struct get_overload_info { using type = invocable_traits_impl< std::decay_t< decltype( invocable_traits_resolve_overload<C>( &C::operator(), Head{} ) ) > > ; }; template <typename C, typename Head> using get_overload_info_t = typename get_overload_info<C, Head>::type; template <bool, std::size_t i, typename C, typename... Args> struct invocable_traits_overload_info_impl { using type = get_overload_info_t<C, std::tuple_element_t<i, std::tuple<Args...>>>; }; template <std::size_t i, typename C, typename... Args> struct invocable_traits_overload_info_impl<false, i, C, Args...> { static_assert(i < sizeof...(Args), "Argument index out of bounds (queried callable does not have this many matching overloads)"); // to reduce excessive compiler error output using type = void; }; template <typename C, bool B, typename... Args> struct invocable_traits_overload_info; template <typename C, bool B, typename... Args> struct invocable_traits_overload_info<C, B, typelist<Args...>> { static constexpr std::size_t num_matched_overloads = sizeof...(Args); static constexpr auto is_exact_match = B; template <std::size_t i> using matched_overload = typename invocable_traits_overload_info_impl< i < sizeof...(Args), i, C, Args... >::type; }; // found at least one overload taking a const/ref qualified version of the specified argument types // that is different from those provided by the library user template <typename C, typename List> struct invocable_traits_extract_try_harder : invocable_traits_impl< // instantiate for the first matched overload std::decay_t< decltype( invocable_traits_resolve_overload<C>( &C::operator(), typename try_harder::get_head<List>::type{} ) ) > > , invocable_traits_overload_info< // but expose all matched overloads C, false, List > {}; // failed to find any overload taking the specified argument types or some const/ref qualified version of them template <typename T> struct invocable_traits_extract_try_harder<T, typelist<>> : // empty list -> no combination of arguments matched an overload invocable_traits_error, invocable_traits_error_overload { static constexpr Error error = get_error<T, true>(); }; // specific overloaded operator() is available, use it for analysis template <typename C, bool, typename... OverloadArgs> struct invocable_traits_extract : invocable_traits_impl< std::decay_t< decltype( invocable_traits_resolve_overload<C>( &C::operator(), typelist<OverloadArgs...>{} ) ) > >, invocable_traits_overload_info< C, true, typelist<typelist<OverloadArgs...>> // expose matched overload through this interface also, for consistency, even though matching procedure was not run > {}; // unambiguous operator() is available, use it for analysis template <typename C, bool B> struct invocable_traits_extract<C, B> : invocable_traits_impl< decltype( &C::operator() ) > {}; // no specific overloaded operator() taking the specified arguments is available, try harder // to see if one can be found that takes some other const/reference qualified version of the // input arguments. template <typename T, typename... OverloadArgs> struct invocable_traits_extract<T, false, OverloadArgs...> : invocable_traits_extract_try_harder< T, typename try_harder::filter<T, // filter list of all argument combinations: leave only resolvable overloads try_harder::list_product_t< // cartesian product of these lists typename try_harder::type_maker_for_typelist< // produce list with all const/ref combinations of each argument OverloadArgs... >::type > >::type > {}; template <typename T> struct invocable_traits_extract<T, false> : invocable_traits_error { static constexpr Error error = get_error<T, false>(); }; // catch all that doesn't match the various function signatures above // If T has an operator(), we go with that. Else, issue error message. template <typename T> struct invocable_traits_impl<T> : invocable_traits_extract< T, HasCallOperator<T>&& CanGetCallOperator<T> > {}; // if overload argument types are provided and needed, use them template <typename T, bool B, typename... OverloadArgs> struct invocable_traits_overload_impl : invocable_traits_extract< T, HasCallOperator<T> && HasSpecificCallOperator<T, OverloadArgs...>, OverloadArgs... > {}; // if they are provided but not needed, ignore them template <typename T, typename... OverloadArgs> struct invocable_traits_overload_impl<T, false, OverloadArgs...> : invocable_traits_impl< T > {}; } template <typename T, typename... OverloadArgs> struct get : detail::invocable_traits_overload_impl< std::remove_reference_t<T>, detail::HasCallOperator<std::decay_t<T>> && !detail::CanGetCallOperator<std::decay_t<T>>, OverloadArgs... > {}; template <typename T, typename... OverloadArgs> struct get<std::reference_wrapper<T>, OverloadArgs...> : detail::invocable_traits_overload_impl< std::remove_reference_t<T>, detail::HasCallOperator<std::decay_t<T>> && !detail::CanGetCallOperator<std::decay_t<T>>, OverloadArgs... > {}; template <typename T> struct get<T> : detail::invocable_traits_impl< std::decay_t<T> > {}; template <typename T> struct get<std::reference_wrapper<T>> : detail::invocable_traits_impl< std::decay_t<T> > {}; } #include <functional> void test(int) {} void test2(int) noexcept {} void testEllipsis(int,...) {} void testOver(int,double) {} void testOver(char,short) {} template <typename T> std::conditional_t<std::is_floating_point_v<T>, double, int64_t> testTemplated(T, char) { return 1; } struct tester { tester() = delete; tester& operator=(const tester&) = delete; int yolo(char) const { return 1; } void yoloEllipsis(char, ...) noexcept {} static long yoloStatic(short) { return 1; } void operator()(int) {} const int field; }; template <typename T> struct testerTemplated { testerTemplated() = delete; testerTemplated& operator=(const testerTemplated&) = delete; template <typename R> int yolo(char, R) const {return 3;} template <typename R> short yolo(double, R, ...) {return 4;} void yoloEllipsis(char, ...) noexcept {} template <typename R> static long yoloStatic(R) { return 1; } void operator()(int) {} const int field; }; struct functorOverloaded { int operator()(const int&, ...) const { return 1; } int operator()(int&&, ...) const { return 1; } int operator()(short, int&&, const char&) { return 1; } int operator()(const tester) { return 1; } }; struct functorTemplated { template <typename... T> int operator()(const T&..., ...) const { return 1; } }; int main() { auto lamb = [](const int&) {return "ret"; }; using type1 = decltype(lamb); using traits1 = invocable_traits::get<type1>; invocable_traits::issue_error<traits1::error>(); static_assert(std::is_same_v<const char*, traits1::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type1, int>, traits1::invoke_result_t>, ""); static_assert(std::is_same_v<traits1::invoke_result_t, traits1::declared_result_t>, ""); static_assert(std::is_same_v<decltype(lamb), traits1::class_t>, ""); static_assert(std::is_same_v<const int&, traits1::arg_t<0>>, ""); static_assert(traits1::is_const, ""); using type1b = decltype(+lamb); using traits1b = invocable_traits::get<type1b>; invocable_traits::issue_error<traits1b::error>(); static_assert(std::is_same_v<const char*, traits1b::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type1b, int>, traits1b::invoke_result_t>, ""); static_assert(std::is_same_v<traits1b::invoke_result_t, traits1b::declared_result_t>, ""); static_assert(std::is_same_v<void, traits1b::class_t>, ""); static_assert(std::is_same_v<const int&, traits1b::arg_t<0>>, ""); static_assert(!traits1b::is_const, ""); using type2 = decltype(&test); using traits2 = invocable_traits::get<type2, int>; invocable_traits::issue_error<traits2::error>(); static_assert(std::is_same_v<void, traits2::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2, int>, traits2::invoke_result_t>, ""); static_assert(std::is_same_v<traits2::invoke_result_t, traits2::declared_result_t>, ""); static_assert(std::is_same_v<int, traits2::arg_t<0>>, ""); using type2b = decltype(&test2); using traits2b = invocable_traits::get<type2b>; invocable_traits::issue_error<traits2b::error>(); static_assert(std::is_same_v<void, traits2b::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2b, int>, traits2b::invoke_result_t>, ""); static_assert(std::is_same_v<traits2b::invoke_result_t, traits2b::declared_result_t>, ""); static_assert(std::is_same_v<void, traits2b::class_t>, ""); static_assert(std::is_same_v<int, traits2b::arg_t<0>>, ""); static_assert(!traits2b::is_variadic, ""); static_assert(traits2b::is_noexcept, ""); using type2c = decltype(&testEllipsis); using traits2c = invocable_traits::get<type2c>; invocable_traits::issue_error<traits2c::error>(); static_assert(std::is_same_v<void, traits2c::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2c, int>, traits2c::invoke_result_t>, ""); static_assert(std::is_same_v<traits2c::invoke_result_t, traits2c::declared_result_t>, ""); static_assert(std::is_same_v<int, traits2c::arg_t<0>>, ""); static_assert(traits2c::is_variadic, ""); auto& fref = test; using type2d = decltype(fref); using traits2d = invocable_traits::get<type2d>; invocable_traits::issue_error<traits2d::error>(); static_assert(std::is_same_v<void, traits2d::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2d, int>, traits2d::invoke_result_t>, ""); static_assert(std::is_same_v<traits2d::invoke_result_t, traits2d::declared_result_t>, ""); static_assert(std::is_same_v<int, traits2d::arg_t<0>>, ""); using type2e = std::reference_wrapper<decltype(test)>; //using dfdag = std::invoke_result_t<type2e, int>; using traits2e = invocable_traits::get<type2e>; invocable_traits::issue_error<traits2e::error>(); static_assert(std::is_same_v<void, traits2e::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2e, int>, traits2e::invoke_result_t>, ""); static_assert(std::is_same_v<traits2e::invoke_result_t, traits2e::declared_result_t>, ""); static_assert(std::is_same_v<int, traits2e::arg_t<0>>, ""); [[maybe_unused]]void (*farr[3])(int) = { &test }; using type2f = decltype(farr[0]); using traits2f = invocable_traits::get<type2f>; invocable_traits::issue_error<traits2f::error>(); static_assert(std::is_same_v<void, traits2f::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2f, int>, traits2f::invoke_result_t>, ""); static_assert(std::is_same_v<traits2f::invoke_result_t, traits2f::declared_result_t>, ""); static_assert(std::is_same_v<int, traits2f::arg_t<0>>, ""); using type2g = decltype(&testTemplated<float>); using traits2g = invocable_traits::get<type2g>; invocable_traits::issue_error<traits2g::error>(); static_assert(std::is_same_v<double, traits2g::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2g, float, int>, traits2g::invoke_result_t>, ""); static_assert(std::is_same_v<traits2g::invoke_result_t, traits2g::declared_result_t>, ""); static_assert(std::is_same_v<void, traits2g::class_t>, ""); static_assert(std::is_same_v<float, traits2g::arg_t<0>>, ""); static_assert(std::is_same_v<char, traits2g::arg_t<1>>, ""); void(&overloadRef)(int, double) = testOver; using type2h = decltype(overloadRef); using traits2h = invocable_traits::get<type2h>; invocable_traits::issue_error<traits2h::error>(); static_assert(std::is_same_v<void, traits2h::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type2h, int, double>, traits2h::invoke_result_t>, ""); static_assert(std::is_same_v<traits2h::invoke_result_t, traits2h::declared_result_t>, ""); static_assert(std::is_same_v<int, traits2h::arg_t<0>>, ""); using type3 = decltype(&tester::yolo); using traits3 = invocable_traits::get<type3>; invocable_traits::issue_error<traits3::error>(); static_assert(std::is_same_v<int, traits3::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3, tester, char>, traits3::invoke_result_t>, ""); static_assert(std::is_same_v<traits3::invoke_result_t, traits3::declared_result_t>, ""); static_assert(std::is_same_v<char, traits3::arg_t<0>>, ""); static_assert(traits3::is_const, ""); using type3a = const volatile decltype(&tester::yolo); using traits3a = invocable_traits::get<type3a>; invocable_traits::issue_error<traits3a::error>(); static_assert(std::is_same_v<int, traits3a::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3a, tester, char>, traits3a::invoke_result_t>, ""); static_assert(std::is_same_v<traits3a::invoke_result_t, traits3a::declared_result_t>, ""); static_assert(std::is_same_v<char, traits3a::arg_t<0>>, ""); static_assert(traits3::is_const, ""); using type3b = decltype(&tester::yoloEllipsis); using traits3b = invocable_traits::get<type3b>; invocable_traits::issue_error<traits3b::error>(); static_assert(std::is_same_v<void, traits3b::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3b, tester, char>, traits3b::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3b, tester, char, char>, traits3b::invoke_result_t>, ""); static_assert(std::is_same_v<traits3b::invoke_result_t, traits3b::declared_result_t>, ""); static_assert(std::is_same_v<tester, traits3b::class_t>, ""); static_assert(std::is_same_v<char, traits3b::arg_t<0>>, ""); static_assert(traits3b::is_variadic, ""); static_assert(traits3b::is_noexcept, ""); using type3c = const volatile decltype(&tester::yoloEllipsis); using traits3c = invocable_traits::get<type3c>; invocable_traits::issue_error<traits3c::error>(); static_assert(std::is_same_v<void, traits3c::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3c, tester, char>, traits3c::invoke_result_t>, ""); static_assert(std::is_same_v<traits3c::invoke_result_t, traits3c::declared_result_t>, ""); static_assert(std::is_same_v<tester, traits3c::class_t>, ""); static_assert(std::is_same_v<char, traits3c::arg_t<0>>, ""); static_assert(traits3c::is_variadic, ""); static_assert(traits3c::is_noexcept, ""); using type3d = decltype(&tester::yoloStatic); using traits3d = invocable_traits::get<type3d>; invocable_traits::issue_error<traits3d::error>(); static_assert(std::is_same_v<long, traits3d::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3d, short>, traits3d::invoke_result_t>, ""); static_assert(std::is_same_v<traits3d::invoke_result_t, traits3d::declared_result_t>, ""); static_assert(std::is_same_v<void, traits3d::class_t>, ""); static_assert(std::is_same_v<short, traits3d::arg_t<0>>, ""); static_assert(!traits3d::is_variadic, ""); int(testerTemplated<char>::*overloadClassPtr)(char, int) const = &testerTemplated<char>::yolo<int>; using type3e = decltype(overloadClassPtr); using traits3e = invocable_traits::get<type3e>; invocable_traits::issue_error<traits3e::error>(); static_assert(std::is_same_v<int, traits3e::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3e, testerTemplated<char>, char, int>, traits3e::invoke_result_t>, ""); static_assert(std::is_same_v<traits3e::invoke_result_t, traits3e::declared_result_t>, ""); static_assert(std::is_same_v<testerTemplated<char>, traits3e::class_t>, ""); static_assert(std::is_same_v<char, traits3e::arg_t<0>>, ""); static_assert(std::is_same_v<int, traits3e::arg_t<1>>, ""); static_assert(traits3e::is_const, ""); static_assert(!traits3e::is_variadic, ""); short(testerTemplated<char>::* const volatile overloadClassPtr2)(double, float, ...) = &testerTemplated<char>::yolo<float>; using type3f = decltype(overloadClassPtr2); using traits3f = invocable_traits::get<type3f>; invocable_traits::issue_error<traits3f::error>(); static_assert(std::is_same_v<short, traits3f::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3f, testerTemplated<char>, double, float>, traits3f::invoke_result_t>, ""); static_assert(std::is_same_v<traits3f::invoke_result_t, traits3f::declared_result_t>, ""); static_assert(std::is_same_v<testerTemplated<char>, traits3f::class_t>, ""); static_assert(std::is_same_v<double, traits3f::arg_t<0>>, ""); static_assert(std::is_same_v<float, traits3f::arg_t<1>>, ""); static_assert(!traits3f::is_const, ""); static_assert(!traits3f::is_volatile, ""); static_assert(traits3f::is_variadic, ""); using type3g = decltype(&testerTemplated<void>::yoloEllipsis); using traits3g = invocable_traits::get<type3g>; invocable_traits::issue_error<traits3g::error>(); static_assert(std::is_same_v<void, traits3g::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3g, testerTemplated<void>, char>, traits3g::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3g, testerTemplated<void>, char, char>, traits3g::invoke_result_t>, ""); static_assert(std::is_same_v<traits3g::invoke_result_t, traits3g::declared_result_t>, ""); static_assert(std::is_same_v<testerTemplated<void>, traits3g::class_t>, ""); static_assert(std::is_same_v<char, traits3g::arg_t<0>>, ""); static_assert(traits3g::is_variadic, ""); static_assert(traits3g::is_noexcept, ""); using type3h = std::reference_wrapper<decltype(&tester::yoloEllipsis)>; using traits3h = invocable_traits::get<type3h>; invocable_traits::issue_error<traits3h::error>(); static_assert(std::is_same_v<void, traits3h::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3h, tester, int>, traits3h::invoke_result_t>, ""); static_assert(std::is_same_v<traits3h::invoke_result_t, traits3h::declared_result_t>, ""); static_assert(std::is_same_v<char, traits3h::arg_t<0>>, ""); static_assert(traits3h::is_variadic, ""); static_assert(traits3h::is_noexcept, ""); static_assert(traits3h::arity == 1, ""); using type3i = decltype(&testerTemplated<char>::yoloStatic<int>); using traits3i = invocable_traits::get<type3i>; invocable_traits::issue_error<traits3i::error>(); static_assert(std::is_same_v<long, traits3i::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type3i, short>, traits3i::invoke_result_t>, ""); static_assert(std::is_same_v<traits3i::invoke_result_t, traits3i::declared_result_t>, ""); static_assert(std::is_same_v<void, traits3i::class_t>, ""); static_assert(std::is_same_v<int, traits3i::arg_t<0>>, ""); static_assert(!traits3i::is_variadic, ""); using type4 = const volatile decltype(&tester::field); using traits4 = invocable_traits::get<type4>; invocable_traits::issue_error<traits4::error>(); static_assert(std::is_same_v<std::invoke_result_t<type4, tester>, traits4::invoke_result_t>, ""); static_assert(!std::is_same_v<traits4::invoke_result_t, traits4::declared_result_t>, ""); static_assert(std::is_same_v<const int&&, traits4::invoke_result_t>, ""); static_assert(traits4::arity == 0, ""); using type4b = std::reference_wrapper<const volatile decltype(&tester::field)>; using traits4b = invocable_traits::get<type4b>; invocable_traits::issue_error<traits4b::error>(); static_assert(std::is_same_v<std::invoke_result_t<type4b, tester>, traits4::invoke_result_t>, ""); static_assert(!std::is_same_v<traits4b::invoke_result_t, traits4::declared_result_t>, ""); static_assert(std::is_same_v<const int&&, traits4b::invoke_result_t>, ""); static_assert(traits4b::arity == 0, ""); using type5 = std::add_rvalue_reference_t<decltype(lamb)>; using traits5 = invocable_traits::get<type5>; invocable_traits::issue_error<traits5::error>(); static_assert(std::is_same_v<const char*, traits5::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type5, int>, traits5::invoke_result_t>, ""); static_assert(std::is_same_v<traits5::invoke_result_t, traits5::declared_result_t>, ""); static_assert(std::is_same_v<const int&, traits5::arg_t<0>>, ""); using type6 = std::add_lvalue_reference_t<decltype(lamb)>; using traits6 = invocable_traits::get<type6>; invocable_traits::issue_error<traits6::error>(); static_assert(std::is_same_v<const char*, traits6::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type6, int>, traits6::invoke_result_t>, ""); static_assert(std::is_same_v<traits6::invoke_result_t, traits6::declared_result_t>, ""); static_assert(std::is_same_v<decltype(lamb), traits6::class_t>, ""); static_assert(std::is_same_v<const int&, traits6::arg_t<0>>, ""); // functors using type7 = tester; using traits7 = invocable_traits::get<type7>; invocable_traits::issue_error<traits7::error>(); static_assert(std::is_same_v<void, traits7::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type7, int>, traits7::invoke_result_t>, ""); static_assert(std::is_same_v<traits7::invoke_result_t, traits7::declared_result_t>, ""); static_assert(std::is_same_v<int, traits7::arg_t<0>>, ""); static_assert(!traits7::is_const, ""); static_assert(!traits7::is_noexcept, ""); using type7b = std::add_lvalue_reference_t<const functorOverloaded>; using traits7b = invocable_traits::get<type7b, int>; invocable_traits::issue_error<traits7b::error>(); static_assert(std::is_same_v<int, traits7b::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type7b, const int&>, traits7b::invoke_result_t>, ""); static_assert(std::is_same_v<traits7b::invoke_result_t, traits7b::declared_result_t>, ""); static_assert(std::is_same_v<const int&, traits7b::arg_t<0>>, ""); static_assert(traits7b::is_const, ""); static_assert(!traits7b::is_noexcept, ""); static_assert(traits7b::is_variadic, ""); static_assert(!traits7b::is_exact_match, ""); static_assert(traits7b::num_matched_overloads == 2, ""); static_assert(std::is_same_v<const int&, traits7b::matched_overload<0>::arg_t<0>>, ""); static_assert(std::is_same_v<int&&, traits7b::matched_overload<1>::arg_t<0>>, ""); using type7c = std::reference_wrapper<const functorOverloaded>; using traits7c = invocable_traits::get<type7c, const int&>; invocable_traits::issue_error<traits7c::error>(); static_assert(std::is_same_v<int, traits7c::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type7c, const int&>, traits7c::invoke_result_t>, ""); static_assert(std::is_same_v<traits7c::invoke_result_t, traits7c::declared_result_t>, ""); static_assert(std::is_same_v<const int&, traits7c::arg_t<0>>, ""); static_assert(traits7c::is_const, ""); static_assert(!traits7c::is_noexcept, ""); static_assert(traits7c::is_variadic, ""); static_assert(traits7c::is_exact_match, ""); static_assert(std::is_same_v<traits7c::arg_t<0>, traits7c::matched_overload<0>::arg_t<0>>, ""); using type7d = std::add_lvalue_reference_t<const functorOverloaded>; using traits7d = invocable_traits::get<type7d, short, int, char>; invocable_traits::issue_error<traits7d::error>(); static_assert(std::is_same_v<int, traits7d::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type7d, const int&>, traits7d::invoke_result_t>, ""); static_assert(std::is_same_v<traits7d::invoke_result_t, traits7b::declared_result_t>, ""); static_assert(std::is_same_v<short, traits7d::arg_t<0>>, ""); static_assert(!traits7d::is_const, ""); static_assert(!traits7d::is_noexcept, ""); static_assert(!traits7d::is_variadic, ""); static_assert(!traits7d::is_exact_match, ""); static_assert(traits7d::num_matched_overloads == 1, ""); static_assert(std::is_same_v<const char&, traits7d::matched_overload<0>::arg_t<2>>, ""); using type7t = functorTemplated; using traits7t = invocable_traits::get<type7t, const int&>; invocable_traits::issue_error<traits7t::error>(); static_assert(std::is_same_v<int, traits7t::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type7t, int>, traits7t::invoke_result_t>, ""); static_assert(std::is_same_v<traits7t::invoke_result_t, traits7t::declared_result_t>, ""); static_assert(std::is_same_v<type7t, traits7t::class_t>, ""); static_assert(std::is_same_v<const int&, traits7t::arg_t<0>>, ""); static_assert(traits7t::is_const, ""); /*using type7e = functorOverloaded; using traits7e = invocable_traits::get<type7e>; invocable_traits::issue_error<traits7e::error>(); static_assert(std::is_same_v<int, traits7e::invoke_result_t>, ""); using type7f = functorOverloaded; using traits7f = invocable_traits::get<type7f, char>; invocable_traits::issue_error<traits7f::error>(); static_assert(std::is_same_v<int, traits7f::invoke_result_t>, ""); static_assert(std::is_same_v<traits7f::arg_t<0>, traits7f::matched_overload<0>::arg_t<0>>, "");*/ auto lamb2 = [](const int&, ...) mutable noexcept {return "ret"; }; using type8 = decltype(lamb2); using traits8 = invocable_traits::get<type8>; invocable_traits::issue_error<traits8::error>(); static_assert(std::is_same_v<const char*, traits8::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type8, int>, traits8::invoke_result_t>, ""); static_assert(std::is_same_v<traits8::invoke_result_t, traits8::declared_result_t>, ""); static_assert(std::is_same_v<const int&, traits8::arg_t<0>>, ""); static_assert(traits8::is_variadic, ""); static_assert(traits8::is_noexcept, ""); static_assert(!traits8::is_const, ""); auto lamb3 = [](const auto&) mutable noexcept {return "ret"; }; using type8b = decltype(lamb3); using traits8b = invocable_traits::get<type8b, const int&>; invocable_traits::issue_error<traits8b::error>(); static_assert(std::is_same_v<const char*, traits8b::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type8b, int>, traits8b::invoke_result_t>, ""); static_assert(std::is_same_v<traits8b::invoke_result_t, traits8b::declared_result_t>, ""); static_assert(std::is_same_v<const int&, traits8b::arg_t<0>>, ""); static_assert(traits8b::is_noexcept, ""); static_assert(!traits8b::is_const, ""); using type8c = const std::function<void(const tester&, int)>; using traits8c = invocable_traits::get<type8c>; invocable_traits::issue_error<traits8c::error>(); static_assert(std::is_same_v<void, traits8c::invoke_result_t>, ""); static_assert(std::is_same_v<std::invoke_result_t<type8c, tester, int>, traits8c::invoke_result_t>, ""); static_assert(std::is_same_v<traits8c::invoke_result_t, traits8c::declared_result_t>, ""); static_assert(std::is_same_v<const tester&, traits8c::arg_t<0>>, ""); static_assert(traits8c::is_const, ""); /*using traits9 = invocable_traits::get<int>; invocable_traits::issue_error<traits9::error>(); static_assert(std::is_same_v<const char*, traits9::invoke_result_t>, ""); static_assert(std::is_same_v<const int&, traits9::arg_t<10>>, "");*/ }
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