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
Clojure
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
Helion
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.44 VS17.14
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)
clad v2.2 (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.44 VS17.14
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 g++ 1.27
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.44 VS17.14
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 P2561)
x86-64 clang (experimental P2998)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3334)
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 (C++26 contracts + GNU extensions)
x86-64 gcc (C++26 contracts)
x86-64 gcc (C++26 reflection)
x86-64 gcc (P2034 lambdas)
x86-64 gcc (Thomas Healy)
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.3.0
x86-64 icx 2025.3.1
x86-64 icx 2025.3.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.15.2
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
// Copyright (c) 2015 Noah Lopez // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) /* This example file has become quite large (and has spilled into msetl_example2.cpp) and holds examples for many data types. Your best bet is probably to use a find/search to get to the data type your interested in. */ #include "msetl_example_defs.h" #include "msetl_example2.h" #include "msetl_example3.h" #ifndef EXCLUDE_MSETL_EXAMPLE #include "mseprimitives.h" #include "mseregistered.h" #include "msecregistered.h" #include "msenorad.h" #include "mserefcounting.h" #include "msescope.h" #include "mseasyncshared.h" #include "msepoly.h" #include "msemsearray.h" #include "msemstdarray.h" #include "msemsevector.h" #include "msemstdvector.h" #include "mseivector.h" #include "msevector_test.h" #include "msemstdstring.h" #include "mseregisteredproxy.h" #include "msenoradproxy.h" /* This block of includes is required for the mse::TRegisteredRefWrapper example */ #include <algorithm> #include <list> #include <vector> #include <iostream> #include <numeric> #include <random> #include <functional> #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4100 4456 4189 4702 ) #endif /*_MSC_VER*/ #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-braces" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wunused-variable" #pragma clang diagnostic ignored "-Wunused-function" #pragma clang diagnostic ignored "-Wunused-but-set-variable" #else /*__clang__*/ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #endif /*__GNUC__*/ #endif /*__clang__*/ class H { public: /* Just an example of a templated member function. In this case it's a static one, but it doesn't have to be. You might consider templating pointer parameter types to give the caller some flexibility as to which kind of (smart/safe) pointer they want to use. */ template<typename _Tpointer> static int foo4(_Tpointer A_ptr) { return A_ptr->b; } template<typename _Tpointer, typename _Tvector> static int foo5(_Tpointer A_ptr, _Tvector& vector_ref) { int tmp = A_ptr->b; int retval = 0; vector_ref.clear(); if (A_ptr) { retval = A_ptr->b; } else { retval = -1; } return retval; } template<class _TString1Pointer, class _TString2Pointer> static std::string foo6(_TString1Pointer i1ptr, _TString2Pointer i2ptr) { return (*i1ptr) + (*i2ptr); } /* This function will be used to demonstrate using rsv::as_a_returnable_fparam() to enable template functions to return one of their function parameters, potentially of the scope reference variety which would otherwise be rejected (with a compile error) as an unsafe return value. */ template<class _TString1Pointer, class _TString2Pointer> static auto longest(const _TString1Pointer string1_ptr, const _TString2Pointer string2_ptr) { auto l_string1_ptr = mse::rsv::as_a_returnable_fparam(string1_ptr); auto l_string2_ptr = mse::rsv::as_a_returnable_fparam(string2_ptr); if (l_string1_ptr->length() > l_string2_ptr->length()) { /* If string1_ptr were a regular TXScopeFixedPointer<mse::nii_string> and we tried to return it directly instead of l_string1_ptr, it would have induced a compile error. */ return mse::return_value(l_string1_ptr); } else { /* mse::return_value() usually just returns its input argument unmolested, but in this case, where the argument was obtained from the mse::rsv::as_a_returnable_fparam() it will convert it back the type of the original function parameter (thereby removing the "returnability" attribute that was added by mse::rsv::as_a_returnable_fparam()). */ return mse::return_value(l_string2_ptr); } } /* This function will be used to demonstrate nested function calls (safely) returning scope pointer/references. */ template<class _TString1Pointer, class _TString2Pointer> static auto nested_longest(const _TString1Pointer string1_ptr, const _TString2Pointer string2_ptr) { auto l_string1_ptr = mse::rsv::as_a_returnable_fparam(string1_ptr); auto l_string2_ptr = mse::rsv::as_a_returnable_fparam(string2_ptr); /* Note that with functions (potentially) returning a scope reference parameter (or an object derived from a scope reference parameter), you generally want the function to be a template function with the scope reference parameters types being (deduced) template parameters, as with this function, rather than more explicitly specified scope reference types or template types. The reason for this is that in the case of nested function calls, the number of nested function calls from which a scope reference object can be (safely) returned is embedded in the scope reference object's type. That is, the exact type of a returnable scope reference object depends on how many (levels of) nested function calls it has been passed through. And you generally want your functions that return scope reference objects to preserve the exact type of the scope reference passed, otherwise you may not be allowed (i.e. induced compile error) to return the scope reference all the way back to the scope it originated from. */ return mse::return_value(longest(l_string1_ptr, l_string2_ptr)); } struct CE { mse::nii_string m_string1 = "abcde"; }; /* This function demonstrates scope reference objects inheriting the "returnability" trait from the reference objects from which they were derived. */ template<class _TPointer1> static auto xscope_string_const_section_to_member_of_CE(const _TPointer1 CE_ptr) { auto returnable_CE_ptr = mse::rsv::as_a_returnable_fparam(CE_ptr); /* "Pointers to members" based on returnable pointers inherit the "returnability". */ auto returnable_cpointer_to_member = mse::make_xscope_const_pointer_to_member_v2(returnable_CE_ptr, &CE::m_string1); /* "scope nrp string const sections" based on returnable pointers (or iterators) inherit the "returnability". */ auto returnable_string_const_section = mse::make_xscope_string_const_section(returnable_cpointer_to_member); /* Subsections of returnable sections inherit the "returnability". */ auto returnable_string_const_section2 = mse::make_xscope_subsection(returnable_string_const_section, 1, 3); return mse::return_value(returnable_string_const_section2); } template<class _TPointer1> static auto nested_xscope_string_const_section_to_member_of_CE(const _TPointer1& CE_ptr) { auto returnable_CE_ptr = mse::rsv::as_a_returnable_fparam(CE_ptr); return mse::return_value(xscope_string_const_section_to_member_of_CE(returnable_CE_ptr)); } /* This function will be used to demonstrate using rsv::as_an_fparam() to enable template functions to accept scope pointers to temporary objects. */ template<class _TPointer1, class _TPointer2> static bool second_is_longer(_TPointer1&& string1_xscpptr, _TPointer2&& string2_xscpptr) { auto l_string1_xscpptr = mse::rsv::as_an_fparam(std::forward<decltype(string1_xscpptr)>(string1_xscpptr)); auto l_string2_xscpptr = mse::rsv::as_an_fparam(std::forward<decltype(string2_xscpptr)>(string2_xscpptr)); return (l_string1_xscpptr->length() > l_string2_xscpptr->length()) ? false : true; } mse::nii_string m_string1 = "initial text"; }; /* User-defined classes need to be declared as (safely) shareable in order to be accepted by the access requesters. */ typedef mse::rsv::TAsyncShareableAndPassableObj<H> ShareableH; int main(int argc, char* argv[]) { mse::msevector_test msevector_test; msevector_test.run_all(); mse::msevector_test2 msevector_test2; msevector_test2.test1(); { /**********************/ /* mstd::vector<> */ /**********************/ /* mse::mstd::vector<> is an almost "completely safe" (bounds checked, iterator checked and memory managed) implementation of std::vector. Here we'll demonstate the safety of the insert() member function. */ double a1[3] = { 1.0, 2.0, 3.0 }; double *d_pointer1 = &(a1[0]); double a2[3] = { 4.0, 5.0, 360 }; double *d_pointer2 = &(a2[0]); mse::mstd::vector<double> v1; //v1.insert(v1.begin(), d_pointer1, d_pointer2); /* not good */ /* std::vector supports "naked" pointers as parameters to the insert() member function so mse::mstd::vector does also. Unfortunately there is no way to ensure that the naked pointer parameters have valid values. */ #ifdef MSVC2010_COMPATIBLE mse::mstd::vector<double> v2(a1, a1+3); mse::mstd::vector<double> v3(a2, a2+3); mse::mstd::vector<double> v4; #else /*MSVC2010_COMPATIBLE*/ mse::mstd::vector<double> v2 = { 1.0, 2.0, 3.0 }; mse::mstd::vector<double> v3 = { 4.0, 5.0, 360 }; mse::mstd::vector<double> v4; #endif /*MSVC2010_COMPATIBLE*/ #ifndef MSE_MSTDVECTOR_DISABLED MSE_TRY { v4.insert(v4.begin(), v2.begin(), v3.begin()); } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; /* The exception is triggered by a comparision of incompatible "safe" iterators. */ } #endif // !MSE_MSTDVECTOR_DISABLED /* And of course the iterators can be used with the standard algorithms, just like those of std::vector. */ std::sort(v3.begin(), v3.end()); { #ifdef MSE_HAS_CXX17 #ifndef MSE_MSTDVECTOR_DISABLED /* deduction guide example */ auto str1 = std::string("abcd"); auto vector2 = mse::mstd::vector(str1.cbegin(), str1.cend()); assert('b' == vector2[1]); #endif // !MSE_MSTDVECTOR_DISABLED #endif /* MSE_HAS_CXX17 */ } } { /* Here's how mse::mstd::vector<>::iterator handles occurrences of "use-after-free". */ typedef mse::mstd::vector<int> vint_type; mse::mstd::vector<vint_type> vvi; { vint_type vi; vi.push_back(5); vvi.push_back(vi); } auto vi_it = vvi[0].begin(); vvi.clear(); #if !defined(MSE_MSTDVECTOR_DISABLED) && !defined(MSE_MSTD_VECTOR_CHECK_USE_AFTER_FREE) MSE_TRY { /* At this point, the vint_type object is cleared from vvi, but (with the current library implementation) it has not actually been deallocated/destructed yet because it "knows" that there is an iterator, namely vi_it, that is still referencing it. It will be deallocated when there are no more iterators referencing it. */ auto value = (*vi_it); /* In debug mode this will fail an assert. In non-debug mode it'll just work (safely). */ assert(5 == value); vint_type vi2; vi_it = vi2.begin(); /* The vint_type object that vi_it was originally pointing to is now deallocated/destructed, because vi_it no longer references it. */ } MSE_CATCH_ANY { /* At present, no exception will be thrown. With future library implementations, maybe. */ std::cerr << "potentially expected exception" << std::endl; } #endif // !defined(MSE_MSTDVECTOR_DISABLED) && !defined(MSE_MSTD_VECTOR_CHECK_USE_AFTER_FREE) } { /* If the vector is declared as a "scope" object (which basically indicates that it is declared on the stack), then you can use "scope" iterators. While there are limitations on when they can be used, scope iterators would be the preferred iterator type where performance is a priority as they don't require extra run-time overhead to ensure that the vector has not been prematurely deallocated. */ /* Here we're declaring an vector as a scope object. */ mse::TXScopeObj<mse::mstd::vector<int>> vector1_scpobj = mse::mstd::vector<int>{ 1, 2, 3 }; { /* Here we're obtaining a scope iterator to the vector. */ auto scp_iter1 = mse::mstd::make_xscope_begin_iterator(&vector1_scpobj); auto scp_iter2 = mse::mstd::make_xscope_end_iterator(&vector1_scpobj); std::sort(scp_iter1, scp_iter2); auto scp_citer3 = mse::mstd::make_xscope_begin_const_iterator(&vector1_scpobj); scp_citer3 = scp_iter1; scp_citer3 = mse::mstd::make_xscope_begin_const_iterator(&vector1_scpobj); scp_citer3 += 2; auto res1 = *scp_citer3; auto res2 = scp_citer3[0]; /* Here we demonstrate the case where the vector is a member of a class/struct declared as a scope object. */ class CContainer1 { public: CContainer1() : m_vector({ 1, 2, 3 }) {} mse::mstd::vector<int> m_vector; }; mse::TXScopeObj<CContainer1> container1_scpobj; auto container1_m_vector_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_vector); auto scp_citer4 = mse::mstd::make_xscope_begin_iterator(container1_m_vector_scpptr); scp_citer4++; auto res3 = *scp_citer4; /* Note that scope iterators, while they exist, have the effect of "structure locking" their associated container. That is, while a scope iterator exists, the "structure" (i.e. size or capacity) of the target container will remain unchanged. Attempting any operation that would affect the structure would result in an exception. This property allows us to (safely) obtain a (direct) scope pointer to the scope iterator's target element. */ auto scp_ptr1 = mse::xscope_pointer(scp_iter1); auto res4 = *scp_ptr1; } /* After all the scope pointers have gone out of scope, you may again perform operations that affect the container's "structure" (i.e. size or capacity). */ vector1_scpobj.push_back(4); } { /*****************/ /* ivector<> */ /*****************/ /* mse::ivector<> is a safe vector like mse::mstd::vector<>, but its iterators behave more like list iterators than standard vector iterators. That is, upon insert or delete, the iterators continue to point to the same item, not (necessarily) the same position. And they don't become "invalid" upon insert or delete, unless the item they point to is deleted. */ #ifdef MSVC2010_COMPATIBLE int a1[4] = { 1, 2, 3, 4 }; mse::ivector<int> v(a1, a1 + 4); #else /*MSVC2010_COMPATIBLE*/ mse::ivector<int> v = { 1, 2, 3, 4 }; #endif /*MSVC2010_COMPATIBLE*/ mse::ivector<int>::ipointer ip1 = v.begin(); ip1 += 2; assert(3 == (*ip1)); auto ip2 = v.begin(); v.erase(ip2); /* remove the first item */ assert(3 == (*ip1)); /* ip1 continues to point to the same item, not the same position */ ip1--; assert(2 == (*ip1)); for (mse::ivector<int>::cipointer cip = v.cbegin(); v.cend() != cip; cip++) { /* You might imagine what would happen if cip were a regular vector iterator. */ v.insert(v.begin(), (*cip)); } /* Btw, the iterators are compatible with stl algorithms, like any other stl iterators. */ std::sort(v.begin(), v.end()); } { /*********************************/ /* us::msevector<>::ipointer */ /*********************************/ /* mse::us::msevector<> is another vector that is highly compatible with std::vector<>. But mse::us::msevector<> also supports a new type of iterator called "ipointer". ipointers make more (intuitive) sense than standard vector iterators. Upon insert or delete, ipointers continue to point to the same item, not (necessarily) the same position. And they don't become "invalid" upon insert or delete, unless the item they point to is deleted. They support all the standard iterator operators, but also have member functions with "friendlier" names. */ #ifdef MSVC2010_COMPATIBLE int a1[4] = { 1, 2, 3, 4 }; mse::us::msevector<int> v1(a1, a1+4); #else /*MSVC2010_COMPATIBLE*/ mse::us::msevector<int> v1 = { 1, 2, 3, 4 }; #endif /*MSVC2010_COMPATIBLE*/ mse::us::msevector<int> v = v1; { mse::us::msevector<int>::ipointer ip1 = v.ibegin(); ip1 += 2; assert(3 == (*ip1)); auto ip2 = v.ibegin(); /* ibegin() returns an ipointer */ v.erase(ip2); /* remove the first item */ assert(3 == (*ip1)); /* ip1 continues to point to the same item, not the same position */ ip1--; assert(2 == (*ip1)); for (mse::us::msevector<int>::cipointer cip = v.cibegin(); v.ciend() != cip; cip++) { /* You might imagine what would happen if cip were a regular vector iterator. */ v.insert(v.ibegin(), (*cip)); } } v = v1; { /* This code block is equivalent to the previous code block, but uses ipointer's more "readable" interface that might make the code a little more clear to those less familiar with C++ syntax. */ mse::us::msevector<int>::ipointer ip_vit1 = v.ibegin(); ip_vit1.advance(2); assert(3 == ip_vit1.item()); auto ip_vit2 = v.ibegin(); v.erase(ip_vit2); assert(3 == ip_vit1.item()); ip_vit1.set_to_previous(); assert(2 == ip_vit1.item()); mse::us::msevector<int>::cipointer cip(v); for (cip.set_to_beginning(); cip.points_to_an_item(); cip.set_to_next()) { v.insert_before(v.ibegin(), (*cip)); } } /* Btw, ipointers are compatible with stl algorithms, like any other stl iterators. */ std::sort(v.ibegin(), v.iend()); /* And just to be clear, mse::us::msevector<> retains its original (high performance) stl::vector iterators. */ std::sort(v.begin(), v.end()); /* mse::us::msevector<> also provides "safe" (bounds checked) versions of the original stl::vector iterators. */ std::sort(v.ss_begin(), v.ss_end()); { /* A "scope" version of the safe iterators can be used when the vector is declared as a scope object. There are limitations on when they can be used, but unlike the other msevector iterators, those restrictions ensure that they won't be used to access the vector after it's been deallocated. */ mse::TXScopeObj<mse::us::msevector<int>> vector1_scpobj = mse::us::msevector<int>{ 1, 2, 3 }; { auto scp_ss_iter1 = mse::make_xscope_begin_iterator(&vector1_scpobj); auto scp_ss_iter2 = mse::make_xscope_end_iterator(&vector1_scpobj); std::sort(scp_ss_iter1, scp_ss_iter2); auto scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&vector1_scpobj); scp_ss_citer3 = scp_ss_iter1; scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&vector1_scpobj); scp_ss_citer3 += 2; auto res1 = *scp_ss_citer3; auto res2 = scp_ss_citer3[0]; /* Here we demonstrate the case where the vector is a member of a class/struct declared as a scope object. */ class CContainer1 { public: CContainer1() : m_vector({ 1, 2, 3 }) {} mse::us::msevector<int> m_vector; }; mse::TXScopeObj<CContainer1> container1_scpobj; auto container1_m_vector_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_vector); auto scp_ss_citer4 = mse::make_xscope_begin_iterator(container1_m_vector_scpptr); scp_ss_citer4++; auto res3 = *scp_ss_citer4; /* Note that scope iterators, while they exist, have the effect of "structure locking" their associated container. That is, while a scope iterator exists, the "structure" (i.e. size or capacity) of the target container will remain unchanged. Attempting any operation that would affect the structure would result in an exception. This property allows us to (safely) obtain a (direct) scope pointer to the scope iterator's target element. */ auto scp_ptr1 = mse::xscope_pointer(scp_ss_iter1); auto res4 = *scp_ptr1; } /* After all the scope pointers have gone out of scope, you may again perform operations that affect the container's "structure" (i.e. size or capacity). */ vector1_scpobj.push_back(4); } } { /*********************/ /* mstd::array<> */ /*********************/ /* mse::mstd::array<> is an almost "completely safe" (bounds checked, iterator checked and "lifespan aware") implementation of std::array. */ /* Here we demonstrate some iterator safety. */ mse::mstd::array<int, 3> a1 = { 1, 2, 3 }; mse::mstd::array<int, 3> a2 = { 11, 12, 13 }; #ifndef MSE_MSTDARRAY_DISABLED MSE_TRY { for (auto it1 = a1.begin(); it1 != a2.end(); it1++) { /* It's not going to make it here. The invalid iterator comparison will throw an exception. */ std::cerr << "unexpected execution" << std::endl; } } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; } #ifndef EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1 /* Here we're demonstrating mse::mstd::array<> and its iterator's "lifespan awareness". */ mse::mstd::array<int, 2>::iterator it1; { mse::mstd::array<int, 2> a3 = { 5 }; /*Notice that initializer lists may contain fewer elements than the array.*/ it1 = a3.begin(); assert(5 == (*it1)); } MSE_TRY{ /* it1 "knows" that its target has been destroyed. It will throw an exception on any attempt to dereference it. */ int i = (*it1); std::cerr << "unexpected execution" << std::endl; } MSE_CATCH_ANY{ std::cerr << "expected exception" << std::endl; } #endif // !EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1 #endif // !MSE_MSTDARRAY_DISABLED /* And of course the iterators can be used with the standard algorithms, just like those of std::array. */ std::sort(a2.begin(), a2.end()); { /* If the array is declared as a "scope" object (which basically indicates that it is declared on the stack), then you can use "scope" iterators. While there are limitations on when they can be used, scope iterators would be the preferred iterator type where performance is a priority as they don't require extra run-time overhead to ensure that the array has not been prematurely deallocated. */ /* Here we're declaring an array as a scope object. */ mse::TXScopeObj<mse::mstd::array<int, 3>> array1_scpobj = mse::mstd::array<int, 3>{ 1, 2, 3 }; // or alternatively: //auto array1_scpobj = mse::make_xscope(mse::mstd::array<int, 3>{ 1, 2, 3 }); /* Here we're obtaining a scope iterator to the array. */ auto scp_array_iter1 = mse::mstd::make_xscope_begin_iterator(&array1_scpobj); auto scp_array_iter2 = mse::mstd::make_xscope_end_iterator(&array1_scpobj); std::sort(scp_array_iter1, scp_array_iter2); auto scp_array_citer3 = mse::mstd::make_xscope_begin_const_iterator(&array1_scpobj); scp_array_citer3 = scp_array_iter1; scp_array_citer3 = mse::mstd::make_xscope_begin_const_iterator(&array1_scpobj); scp_array_citer3 += 2; auto res1 = *scp_array_citer3; auto res2 = scp_array_citer3[0]; /* Here we demonstrate the case where the array is a member of a class/struct declared as a scope object. */ class CContainer1 { public: CContainer1() : m_array{ 1, 2, 3 } {} mse::mstd::array<int, 3> m_array/* = { 1, 2, 3 }*/; }; mse::TXScopeObj<CContainer1> container1_scpobj; auto container1_m_array_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_array); auto scp_iter4 = mse::mstd::make_xscope_begin_iterator(container1_m_array_scpptr); scp_iter4++; auto res3 = *scp_iter4; /* You can also obtain a corresponding scope pointer from a scope iterator. */ auto scp_ptr1 = mse::xscope_pointer(scp_iter4); auto res4 = *scp_ptr1; auto scp_cptr1 = mse::xscope_const_pointer(scp_iter4); auto res5 = *scp_cptr1; } mse::mstd::array_test testobj1; testobj1.test1(); } { /**********************/ /* us::msearray<> */ /**********************/ /* mse::us::msearray<> is another array implementation that's not quite as safe as mse::mstd::array<> in the sense that its iterators are not "lifespan aware" (i.e. could be used to access an array after it's been deallocated). And it provides both "safe" (bounds-checked) and unsafe iterators. Basically, mse::us::msearray<> is a compromise between performance and safety. */ mse::us::msearray<int, 3> a1 = { 1, 2, 3 }; mse::us::msearray<int, 3> a2 = { 11, 12, 13 }; //bool bres1 = (a1.begin() == a2.end()); /* The previous commented out line would result in "undefined behavior. */ MSE_TRY { /* The behavior of the next line is not "undefined". It's going to throw an exception. */ bool bres2 = (a1.ss_begin() == a2.ss_end()); } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; } auto ss_cit1 = a1.ss_cbegin(); /* These safe iterators support traditional and "friendly" iterator operation syntax. */ ss_cit1++; ss_cit1.set_to_next(); /*same as previous line*/ ss_cit1.set_to_beginning(); bool bres3 = ss_cit1.has_previous(); ss_cit1.set_to_end_marker(); bool bres4 = ss_cit1.points_to_an_item(); std::sort(a2.ss_begin(), a2.ss_end()); { /* A "scope" version of the safe iterators can be used when the array is declared as a scope object. There are limitations on when they can be used, but unlike the other msearray iterators, those restrictions ensure that they won't be used to access the array after it's been deallocated. */ mse::TXScopeObj<mse::us::msearray<int, 3>> array1_scpobj = mse::us::msearray<int, 3>{ 1, 2, 3 }; auto scp_ss_iter1 = mse::make_xscope_begin_iterator(&array1_scpobj); auto scp_ss_iter2 = mse::make_xscope_end_iterator(&array1_scpobj); std::sort(scp_ss_iter1, scp_ss_iter2); auto scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&array1_scpobj); scp_ss_citer3 = scp_ss_iter1; scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&array1_scpobj); scp_ss_citer3 += 2; auto res1 = *scp_ss_citer3; auto res2 = scp_ss_citer3[0]; /* Here we demonstrate the case where the array is a member of a class/struct declared as a scope object. */ class CContainer1 { public: CContainer1() : m_array({ 1, 2, 3 }) {} mse::us::msearray<int, 3> m_array; }; mse::TXScopeObj<CContainer1> container1_scpobj; auto container1_m_array_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_array); auto scp_ss_citer4 = mse::make_xscope_begin_iterator(container1_m_array_scpptr); scp_ss_citer4++; auto res3 = *scp_ss_citer4; /* You can also obtain a corresponding scope pointer from a scope iterator. */ auto scp_ptr1 = mse::xscope_pointer(scp_ss_citer4); auto res4 = *scp_ptr1; auto scp_cptr1 = mse::xscope_const_pointer(scp_ss_citer4); auto res5 = *scp_cptr1; } mse::msearray_test testobj1; testobj1.test1(); } { /*******************************/ /* CInt, CSize_t and CBool */ /*******************************/ /* The unsigned types like size_t can cause insidious bugs due to the fact that they can cause signed integers to be implicitly converted to unsigned. msetl provides substitutes for size_t and int that change the implicit conversion to instead be from unsigned to signed. */ mse::self_test::CPrimitivesTest1::s_test1(); { size_t number_of_security_credits = 0; number_of_security_credits += 5; int minimum_number_of_security_credits_required_for_access = 7; bool access_granted = false; if (number_of_security_credits - minimum_number_of_security_credits_required_for_access >= 0) { /* You may not even get a compiler warning about the implicit conversion from (signed) int to (unsigned) size_t. */ access_granted = true; /*oops*/ } else { access_granted = false; assert(false); } } { mse::CNDSize_t number_of_security_credits = 0; number_of_security_credits += 5; int minimum_number_of_security_credits_required_for_access = 7; bool access_granted = false; if (number_of_security_credits - minimum_number_of_security_credits_required_for_access >= 0) { access_granted = true; assert(false); } else { access_granted = false; /* that's better */ } } { size_t number_of_security_credits = 0; number_of_security_credits += 5; mse::CNDInt minimum_number_of_security_credits_required_for_access = 7; mse::CNDBool access_granted = false; if (number_of_security_credits - minimum_number_of_security_credits_required_for_access >= 0) { access_granted = true; assert(false); } else { access_granted = false; /* this works too */ } } mse::CSize_t mse_szt1 = 0; /* size_t szt2 = mse_szt1; */ /* This wouldn't compile. */ size_t szt1 = mse::as_a_size_t(mse_szt1); /* We exclude automatic conversion from mse::CSize_t to size_t because we consider size_t an intrinsically error prone type. */ #ifndef MSVC2010_COMPATIBLE size_t szt2 = static_cast<size_t>(mse_szt1); /* We exclude automatic conversion from mse::CSize_t to size_t because we consider size_t an intrinsically error prone type. */ #endif // !MSVC2010_COMPATIBLE MSE_TRY { mse::CNDSize_t mse_szt2 = 0; mse_szt2 = -3; } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; /* The exception is triggered by an "out of range" assignment to an mse::CSize_t. */ } MSE_TRY { mse::CSize_t mse_szt3 = 3; mse_szt3 -= 1; /* this is fine */ mse_szt3 -= 4; /* this is gonna throw an exception */ } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; /* The exception is triggered by an attempt to set an mse::CSize_t to an "out of range" value. */ } } { /**************************/ /* TRegisteredPointer */ /**************************/ /* For safety reasons we want to avoid using native pointers. "Managed" pointers like std:shared_ptr are an alternative, but sometimes you don't want a pointer that takes ownership (of the object's lifespan). So we provide mse::TRegisteredPointer. Because it doesn't take ownership, it can be used with objects allocated on the stack and is compatible with raii techniques. Also, in most cases, it can be used as a compatible, direct substitute for native pointers, making it straightforward to update legacy code. Proper "const", "not null" and "fixed" (non-retargetable) versions are provided as well.*/ class A { public: virtual ~A() {} int b = 3; }; class B { public: static int foo1(A* a_native_ptr) { return a_native_ptr->b; } static int foo2(mse::TRegisteredPointer<A> A_registered_ptr) { return A_registered_ptr->b; } /* mse::TRegisteredFixedConstPointer<A> is recommended where you might have used "const A&".*/ static int foo3(mse::TRegisteredFixedConstPointer<A> A_registered_fc_ptr) { return A_registered_fc_ptr->b; } }; A* A_native_ptr = nullptr; /* mse::TRegisteredPointer<> is basically a "safe" version of the native pointer. */ mse::TRegisteredPointer<A> A_registered_ptr1; { A a; mse::TRegisteredObj<A> registered_a; /* mse::TRegisteredObj<A> is a class that is publicly derived from A, and so should be a compatible substitute for A in almost all cases. */ /* You can also use the make_registered() function to obtain a registered object from a given value. */ auto registered_a2 = mse::make_registered(A()); assert(a.b == registered_a.b); A_native_ptr = &a; A_registered_ptr1 = ®istered_a; assert(A_native_ptr->b == A_registered_ptr1->b); mse::TRegisteredPointer<A> A_registered_ptr2 = ®istered_a; A_registered_ptr2 = nullptr; #ifndef MSE_REGISTEREDPOINTER_DISABLED MSE_TRY { int i = A_registered_ptr2->b; /* this is gonna throw an exception */ } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; /* The exception is triggered by an attempt to dereference a null "registered pointer". */ } #endif // !MSE_REGISTEREDPOINTER_DISABLED B::foo3(®istered_a); /* mse::TRegisteredPointers don't convert directly into mse::TRegisteredFixedConstPointers because mse::TRegisteredFixedConstPointers are never supposed to be null, where mse::TRegisteredPointers can be. But you can easily obtain an mse::TRegisteredFixedPointer which does convert to an mse::TRegisteredFixedConstPointer. */ B::foo3(&*A_registered_ptr1); /* Functions can be templated to allow the caller to use the (smart/safe) pointer of their choice. */ H::foo4<mse::TRegisteredFixedConstPointer<A>>(&*A_registered_ptr1); /* You don't actually need to explicitly specify the template type. */ H::foo4(&*A_registered_ptr1); if (A_registered_ptr2) { assert(false); } else if (A_registered_ptr2 != A_registered_ptr1) { A_registered_ptr2 = A_registered_ptr1; assert(A_registered_ptr2 == A_registered_ptr1); } } #ifndef MSE_REGISTEREDPOINTER_DISABLED MSE_TRY { /* A_registered_ptr1 "knows" that the (registered) object it was pointing to has now been deallocated. */ int i = A_registered_ptr1->b; /* So this is gonna throw an exception */ } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; } #endif // !MSE_REGISTEREDPOINTER_DISABLED { /* For heap allocations mse::registered_new is kind of analagous to std::make_shared, but again, mse::TRegisteredPointers don't take ownership so you are responsible for deallocation. */ auto A_registered_ptr3 = mse::registered_new<A>(); assert(3 == A_registered_ptr3->b); mse::registered_delete<A>(A_registered_ptr3); #ifndef MSE_REGISTEREDPOINTER_DISABLED MSE_TRY { /* A_registered_ptr3 "knows" that the (registered) object it was pointing to has now been deallocated. */ int i = A_registered_ptr3->b; /* So this is gonna throw an exception */ } MSE_CATCH_ANY { std::cerr << "expected exception" << std::endl; } #endif // !MSE_REGISTEREDPOINTER_DISABLED } { /* Remember that registered pointers can only point to registered objects. So, for example, if you want a registered pointer to an object's base class object, that base class object has to be a registered object. */ class DA : public mse::TRegisteredObj<A> {}; mse::TRegisteredObj<DA> registered_da; mse::TRegisteredPointer<DA> DA_registered_ptr1 = ®istered_da; mse::TRegisteredPointer<A> A_registered_ptr4 = DA_registered_ptr1; A_registered_ptr4 = ®istered_da; class D : public A {}; mse::TRegisteredObj<D> registered_d; mse::TRegisteredPointer<D> D_registered_ptr1 = ®istered_d; /* The next commented out line of code is not going to work because D's base class object is not a registered object. */ //mse::TRegisteredPointer<A> A_registered_ptr5 = D_registered_ptr1; } { /* Obtaining safe pointers to members of registered objects: */ class E { public: virtual ~E() {} mse::TRegisteredObj<std::string> reg_s = "some text "; std::string s2 = "some other text "; }; mse::TRegisteredObj<E> registered_e; mse::TRegisteredPointer<E> E_registered_ptr1 = ®istered_e; /* To obtain a safe pointer to a member of a registered object you could just make the member itself a registered object. */ mse::TRegisteredPointer<std::string> reg_s_registered_ptr1 = &(E_registered_ptr1->reg_s); /* Or you can use the "mse::make_pointer_to_member_v2()" function. */ auto s2_safe_ptr1 = mse::make_pointer_to_member_v2(E_registered_ptr1, &E::s2); (*s2_safe_ptr1) = "some new text"; auto s2_safe_const_ptr1 = mse::make_const_pointer_to_member_v2(E_registered_ptr1, &E::s2); /* The return type of mse::make_pointer_to_member_v2() depends on the type of the parameters passed to it. In this case, the type of s2_safe_ptr1 is mse::us::TSyncWeakFixedPointer<std::string, mse::TRegisteredPointer<E>>. s2_safe_ptr1 here is essentially a pointer to "E.s2" (string member of class E) with a registered pointer to E to in its pocket. It uses the registered pointer to ensure that it is safe to access the object. */ /* In practice, rather than declaring a specific mse::us::TSyncWeakFixedPointer parameter, we expect functions intended for general use to be "templatized" so that they can accept any type of pointer. */ std::string res1 = H::foo6(s2_safe_ptr1, s2_safe_const_ptr1); } { /***************************/ /* TCRegisteredPointer */ /***************************/ class C; class D { public: virtual ~D() {} mse::TCRegisteredPointer<C> m_c_ptr; }; class C { public: mse::TCRegisteredPointer<D> m_d_ptr; }; mse::TCRegisteredObj<C> regobjfl_c; mse::TCRegisteredPointer<D> d_ptr = mse::cregistered_new<D>(); regobjfl_c.m_d_ptr = d_ptr; d_ptr->m_c_ptr = ®objfl_c; mse::cregistered_delete<D>(d_ptr); { /* Polymorphic conversions. */ class FD : public mse::TCRegisteredObj<D> {}; mse::TCRegisteredObj<FD> cregistered_fd; mse::TCRegisteredPointer<FD> FD_cregistered_ptr1 = &cregistered_fd; mse::TCRegisteredPointer<D> D_cregistered_ptr4 = FD_cregistered_ptr1; D_cregistered_ptr4 = &cregistered_fd; mse::TCRegisteredFixedPointer<D> D_cregistered_fptr1 = &cregistered_fd; mse::TCRegisteredFixedConstPointer<D> D_cregistered_fcptr1 = &cregistered_fd; } } mse::self_test::CRegPtrTest1::s_test1(); mse::self_test::CCRegPtrTest1::s_test1(); } { /*********************/ /* TNoradPointer */ /*********************/ /* mse::TNoradPointer<>, like mse::TCRegisteredPointer<>, behaves similar to native pointers. But where registered pointers are automatically set to nullptr when their target is destroyed, the destruction of an object while a "norad" pointer is targeting it results in program termination. This drastic consequence allows norad pointers' run-time safety mechanism to be very lightweight (compared to that of registered pointers). */ class C; class D { public: virtual ~D() {} mse::TNoradPointer<C> m_c_ptr; }; class C { public: mse::TNoradPointer<D> m_d_ptr; }; mse::TNoradObj<C> noradobj_c; mse::TNoradPointer<D> d_ptr = mse::norad_new<D>(); noradobj_c.m_d_ptr = d_ptr; d_ptr->m_c_ptr = &noradobj_c; /* We must make sure that there are no other references to the target of d_ptr before deleting it. Registered pointers don't have the same requirement. */ noradobj_c.m_d_ptr = nullptr; mse::norad_delete<D>(d_ptr); /* You can also use the make_norad() function to obtain a norad object from a given value. */ auto noradobj_c2 = mse::make_norad(C()); { /* Polymorphic conversions. */ class FD : public mse::TNoradObj<D> {}; mse::TNoradObj<FD> norad_fd; mse::TNoradPointer<FD> FD_norad_ptr1 = &norad_fd; mse::TNoradPointer<D> D_norad_ptr4 = FD_norad_ptr1; D_norad_ptr4 = &norad_fd; mse::TNoradFixedPointer<D> D_norad_fptr1 = &norad_fd; mse::TNoradFixedConstPointer<D> D_norad_fcptr1 = &norad_fd; } } mse::self_test::CNoradPtrTest1::s_test1(); #if defined(MSEREGISTEREDREFWRAPPER) && !defined(MSE_PRIMITIVES_DISABLED) { /*****************************/ /* TRegisteredRefWrapper */ /*****************************/ /* Stl provides a copyable, assignable wrapper for C++ references called std::reference_wrapper. std::reference_wrappers, like native C++ references, are not completely safe in the sense that the object they refer to can be deallocated while a reference to it is still available. So we provide mse::TRegisteredRefWrapper, a safe implementation of std::reference_wrapper that "knows" when the object being referenced has been deallocated and will throw an exception on any attempt to access the object after it has been destroyed. In most cases it is probably preferable to just use mse::TRegisteredFixedPointer instead of mse::TRegisteredRefWrapper. */ { #ifndef __apple_build_version__ /* This example originally comes from http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper. */ std::list<mse::TRegisteredObj<mse::CInt>> l(10); std::iota(l.begin(), l.end(), -4); std::vector<mse::TRegisteredRefWrapper<mse::CInt>> v(l.begin(), l.end()); // can't use shuffle on a list (requires random access), but can use it on a vector std::shuffle(v.begin(), v.end(), std::mt19937{ std::random_device{}() }); std::cout << '\n'; std::cout << "TRegisteredRefWrapper test output: \n"; std::cout << "Contents of the list: "; for (auto n : l) { std::cout << n << ' '; } std::cout << '\n'; std::cout << "Contents of the list, as seen through a shuffled vector: "; for (auto i : v) { std::cout << static_cast<mse::CInt&>(i) << ' '; } std::cout << '\n'; std::cout << "Doubling the values in the initial list...\n"; for (auto& i : l) { i *= 2; } std::cout << "Contents of the list, as seen through a shuffled vector: "; for (auto i : v) { std::cout << static_cast<mse::CInt&>(i) << ' '; } std::cout << '\n'; std::cout << '\n'; #endif // !__apple_build_version__ } { /* This example originally comes from http://www.cplusplus.com/reference/functional/reference_wrapper/. */ mse::TRegisteredObj<mse::CInt> a(10), b(20), c(30); // an array of "references": mse::TRegisteredRefWrapper<mse::CInt> refs[] = { a,b,c }; std::cout << "refs:"; for (mse::CInt& x : refs) std::cout << ' ' << x; std::cout << '\n'; mse::TRegisteredObj<mse::CInt> foo(10); auto bar = mse::registered_ref(foo); //++bar; //++(mse::TRegisteredObj<mse::CInt>&)bar; ++(static_cast<mse::CInt&>(bar)); std::cout << foo << '\n'; } } #endif // defined(MSEREGISTEREDREFWRAPPER) && !defined(MSE_PRIMITIVES_DISABLED) { /*****************************/ /* TRefCountingPointer */ /*****************************/ /* TRefCountingPointer behaves similar to std::shared_ptr. Some differences being that it foregoes any thread safety mechanisms, it does not accept raw pointer assignment or construction (use make_refcounting<>() instead), and it will throw an exception on attempted nullptr dereference. And it's smaller and faster. And like TRegisteredPointer, proper "const", "not null" and "fixed" (non-retargetable) versions are provided as well. */ class A { public: A() {} virtual ~A() { int q = 3; /* just so you can place a breakpoint if you want */ } int b = 3; std::string s = "some text "; }; typedef std::vector<mse::TRefCountingFixedPointer<A>> CRCFPVector; class B { public: static int foo1(mse::TRefCountingPointer<A> A_refcounting_ptr, CRCFPVector& rcfpvector_ref) { rcfpvector_ref.clear(); int retval = A_refcounting_ptr->b; A_refcounting_ptr = nullptr; /* Target object is destroyed here. */ return retval; } static std::string foo2(mse::us::TStrongFixedPointer<std::string, mse::TRefCountingFixedPointer<A>> strong_string_ptr, CRCFPVector& rcfpvector_ref) { rcfpvector_ref.clear(); std::string retval = (*strong_string_ptr); return retval; } }; { CRCFPVector rcfpvector; { mse::TRefCountingFixedPointer<A> A_refcountingfixed_ptr1 = mse::make_refcounting<A>(); rcfpvector.push_back(A_refcountingfixed_ptr1); /* Just to demonstrate conversion between refcounting pointer types. */ mse::TRefCountingConstPointer<A> A_refcountingconst_ptr1 = A_refcountingfixed_ptr1; } B::foo1(rcfpvector.front(), rcfpvector); } { /* Obtaining a safe pointer to a member of an object owned by a reference counting pointer: */ CRCFPVector rcfpvector; { mse::TRefCountingFixedPointer<A> A_refcountingfixed_ptr1 = mse::make_refcounting<A>(); rcfpvector.push_back(A_refcountingfixed_ptr1); } /* You can use the "mse::make_pointer_to_member_v2()" function to obtain a safe pointer to a member of an object owned by a refcounting pointer. */ auto s_safe_ptr1 = mse::make_pointer_to_member_v2(rcfpvector.front(), &A::s); (*s_safe_ptr1) = "some new text"; auto s_safe_const_ptr1 = mse::make_const_pointer_to_member_v2(rcfpvector.front(), &A::s); /* The return type of mse::make_pointer_to_member_v2() depends on the type of the parameters passed to it. In this case, the type of s_safe_ptr1 is mse::us::TStrongFixedPointer<std::string, mse::TRefCountingFixedPointer<A>>. s_safe_ptr1 here is essentially a pointer to rcfpvector.front()->s with a copy of rcfpvector.front() welded to it to make sure that the object is not deallocated while s_safe_ptr1 is still around. */ B::foo2(s_safe_ptr1, rcfpvector); /* In practice, rather than declaring a specific mse::TStrongFixedPointer parameter, we expect functions intended for general use to be "templatized" so that they can accept any type of pointer. */ std::string res1 = H::foo6(s_safe_ptr1, s_safe_const_ptr1); /* Obtaining a scope pointer to a member of an object owned by a reference counting pointer */ auto strong_store1 = mse::make_xscope_strong_pointer_store(s_safe_ptr1); auto s_scpptr1 = strong_store1.xscope_ptr(); } mse::TRefCountingPointer_test TRefCountingPointer_test1; bool TRefCountingPointer_test1_res = TRefCountingPointer_test1.testBehaviour(); TRefCountingPointer_test1_res &= TRefCountingPointer_test1.testLinked(); TRefCountingPointer_test1.test1(); } { /* Using registered pointers as weak pointers with reference counting pointers. */ /* TRefCountingPointer<> does not have an associated weak pointer like std::shared_ptr<> does. If you need weak pointer functionality you could just resort to using std::shared_ptr<> and std::weak_ptr<>. When operating within a thread, you could also use registered pointers as weak pointers for TRefCountingPointer<>, which may be preferable in some cases. Generally you're going to want to obtain a "strong" pointer from the weak pointer, so rather than targeting the registered pointer directly at the object of interest, you'd target a/the strong owning pointer of the object. */ typedef mse::TRefCountingFixedPointer<std::string> str_rc_ptr_t; // owning pointer of a string typedef mse::TNDRegisteredObj<str_rc_ptr_t> str_rc_ptr_regobj_t; // registered version of above so that you can obtain a (weak) // registered pointer to it /* str_rc_rc_ptr1 is a "shared" owner of an owning pointer of a string */ auto str_rc_rc_ptr1 = mse::make_nullable_refcounting<str_rc_ptr_regobj_t>(str_rc_ptr_regobj_t(mse::make_refcounting<std::string>("some text"))); /* You need to double dereference it to access the string value. */ std::cout << **str_rc_rc_ptr1 << std::endl; /* Here we're obtaining a (weak) registered pointer to the owning pointer of the string. */ auto str_rc_reg_ptr1 = &(*str_rc_rc_ptr1); /* Here you also need to double dereference it to access the string value. */ std::cout << **str_rc_reg_ptr1 << std::endl; { /* We can obtain a (strong) owning pointer of the string from the (weak) registered pointer. */ auto str_rc_ptr2 = *str_rc_reg_ptr1; std::cout << *str_rc_ptr2 << std::endl; } assert(str_rc_reg_ptr1); // just asserting the str_rc_reg_ptr1 is not null here /* Here we're releasing ownership of the string owning pointer. Since this was its only owner, the string owning pointer (and consequently the string) will be destroyed. */ str_rc_rc_ptr1 = nullptr; assert(!str_rc_reg_ptr1); // here we're asserting that str_rc_reg_ptr1 has been (automatically) set to null } { /* Here we demonstrate using TNDRegisteredPointer<> as a safe "weak_ptr" to prevent cyclic references from becoming memory leaks. This isn't much different from using std::weak_ptr<> in terms of functionality, but there can be performance and safety advantages. */ class CRCNode; typedef mse::TRefCountingFixedPointer<CRCNode> rcnode_strongptr_t; // owning pointer of a CRCNode typedef mse::TNDRegisteredObj<rcnode_strongptr_t> rcnode_strongptr_regobj_t; // registered version of above so that you can obtain a (weak) // registered pointer to it typedef mse::TNDRegisteredPointer<rcnode_strongptr_t> rcnode_strongptr_weakptr_t; // (weak) registered pointer to owning pointer of a CRCNode class CRCNode { public: CRCNode(mse::TRegisteredPointer<mse::CInt> node_count_ptr , rcnode_strongptr_weakptr_t root_ptr_ptr) : m_node_count_ptr(node_count_ptr), m_root_ptr_ptr(root_ptr_ptr) { (*node_count_ptr) += 1; } CRCNode(mse::TRegisteredPointer<mse::CInt> node_count_ptr) : m_node_count_ptr(node_count_ptr) { (*node_count_ptr) += 1; } virtual ~CRCNode() { (*m_node_count_ptr) -= 1; } static rcnode_strongptr_regobj_t MakeRoot(mse::TRegisteredPointer<mse::CInt> node_count_ptr) { auto retval = rcnode_strongptr_regobj_t{ mse::make_refcounting<CRCNode>(node_count_ptr) }; (*retval).m_root_ptr_ptr = &retval; return retval; } static auto MaybeStrongChildPtr(const rcnode_strongptr_regobj_t this_ptr) { return this_ptr->m_maybe_child_ptr; } static rcnode_strongptr_regobj_t MakeChild(const rcnode_strongptr_regobj_t this_ptr) { /* While the library's reference counting pointers' run-time mechansims ensure that they can be dereferenced safely, that safety would not generally extend to any raw references or pointers derived from such a dereference. That would include any implicit (member function) `this` pointers. (You could imagine a mischievous destructor causing the destruction of the `this` object before the end of the member function call.) Avoiding implicit `this` raw pointers could be a reason to prefer free functions over member functions. Unfortunately, the strong imperative to conform to the standard library interface means that library provides corresponding member functions. The following code needs to call the `mstd::optional::emplace()` member function. So we use a "strong pointer store" to ensure that the `this` pointer remains valid for the entire member function call. */ auto xs_store_this = mse::make_xscope_strong_pointer_store(this_ptr); xs_store_this.xscope_ptr()->m_maybe_child_ptr.emplace(rcnode_strongptr_regobj_t{ mse::make_refcounting<CRCNode>(this_ptr->m_node_count_ptr, this_ptr->m_root_ptr_ptr) }); return xs_store_this.xscope_ptr()->m_maybe_child_ptr.value(); } static void DisposeOfChild(const rcnode_strongptr_regobj_t this_ptr) { auto xs_store_this = mse::make_xscope_strong_pointer_store(this_ptr); xs_store_this.xscope_ptr()->m_maybe_child_ptr.reset(); } private: mse::TRegisteredPointer<mse::CInt> m_node_count_ptr; rcnode_strongptr_weakptr_t m_root_ptr_ptr; #if defined(EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1) && defined(MSE_HAS_CXX17) /* msvc2019 seems to have introduced a bug in its "intellisense" feature where it sometimes has difficulty dealing with the library's (safe) optional<> types. The compiler has no problem with it, just the "intellisense" feature. */ #define CRCNODE_STD_OPTIONAL std::optional #else // defined(EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1) && defined(MSE_HAS_CXX17) #define CRCNODE_STD_OPTIONAL mse::mstd::optional #endif // defined(EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1) && defined(MSE_HAS_CXX17) CRCNODE_STD_OPTIONAL<rcnode_strongptr_regobj_t> m_maybe_child_ptr; }; mse::TRegisteredObj<mse::CInt> node_counter = 0; { auto root_owner_ptr = CRCNode::MakeRoot(&node_counter); auto kid1 = root_owner_ptr->MakeChild(root_owner_ptr); { auto kid2 = kid1->MakeChild(kid1); auto kid3 = kid2->MakeChild(kid2); } assert(4 == node_counter); kid1->DisposeOfChild(kid1); assert(2 == node_counter); } assert(0 == node_counter); } { /*****************************/ /* TSingleOwnerPointer */ /*****************************/ /* TSingleOwnerPointer behaves similar to std::unique_ptr. Some differences being that it does not accept raw pointer assignment or construction (use make_single_owner<>() instead), and it will throw an exception on attempted nullptr dereference. */ class A { public: A() {} virtual ~A() { int q = 3; /* just so you can place a breakpoint if you want */ } int b = 3; std::string s = "some text "; }; typedef std::vector<mse::TSingleOwnerPointer<A>> CSOPVector; class B { public: static int foo1(mse::TSingleOwnerPointer<A> A_single_owner_ptr, CSOPVector& sopvector_ref) { assert(!bool(sopvector_ref.front())); int retval = A_single_owner_ptr->b; A_single_owner_ptr = nullptr; /* Target object is destroyed here. */ return retval; } }; { CSOPVector sopvector; { mse::TSingleOwnerPointer<A> A_single_owner_ptr1 = mse::make_single_owner<A>(); sopvector.push_back(std::move(A_single_owner_ptr1)); assert(!bool(A_single_owner_ptr1)); } B::foo1(std::move(sopvector.front()), sopvector); assert(!bool(sopvector.front())); } { mse::TSingleOwnerPointer<A> A_single_owner_ptr1 = mse::make_single_owner<A>(); /* Obtaining a scope pointer to an object owned by a "single_owner" pointer */ auto strong_store1 = mse::make_xscope_borrowing_strong_pointer_store(&A_single_owner_ptr1); auto s_scpptr1 = strong_store1.xscope_ptr(); } mse::TSingleOwnerPointer_test TSingleOwnerPointer_test1; TSingleOwnerPointer_test1.test1(); } { /*****************************/ /* TXScopeFixedPointer */ /*****************************/ /* The "xscope" templates basically allow the programmer to indicate that the target object has "scope lifetime". That is, the object is either allocated on the stack, or its "owner" pointer is allocated on the stack. Scope pointers may only point to scope objects (or certain other objects known to live beyond the scope in question). While there are limitations on when they can be used, scope pointers would be the preferred pointer type where performance is a priority as they don't require any run time overhead to ensure that they will not be used to access a target object has already been deallocated. */ class A { public: A(int x) : b(x) {} virtual ~A() {} bool operator<(const A& _X) const { return (b < _X.b); } int b = 3; std::string s = "some text "; }; class B { public: static int foo1(A* a_native_ptr) { return a_native_ptr->b; } static int foo2(mse::TXScopeFixedPointer<A> A_scpfptr) { return A_scpfptr->b; } static int foo3(mse::TXScopeFixedConstPointer<A> A_scpfcptr) { return A_scpfcptr->b; } }; /* Here we're declaring a scope object. */ mse::TXScopeObj<A> a_scpobj(5); /* note that the '&' ("ampersand") operator is overloaded to return a mse::TXScopeFixedPointer<> */ int res1 = (&a_scpobj)->b; int res2 = B::foo2(&a_scpobj); int res3 = B::foo3(&a_scpobj); /* mse::TXScopeOwnerPointer<> will allocate a scope object on the heap (and deallocate it at the end of the scope). */ /* You can either pass the object's constructor arguments to mse::TXScopeOwnerPointer<>'s constructor, */ mse::TXScopeOwnerPointer<A> xscp_a_ownerptr(7); /* or you can use mse::make_xscope_owner<>() in a manner akin to std::make_unique<>() */ auto xscp_a_ownerptr2 = mse::make_xscope_owner<A>(7); int res4 = B::foo2(xscp_a_ownerptr); int res4b = B::foo2(&(*xscp_a_ownerptr)); /* You can also use the make_xscope() function to obtain a scope object from a given value. */ auto a2_scpobj = mse::make_xscope(A(7)); /* You can use the "mse::make_xscope_pointer_to_member_v2()" function to obtain a safe pointer to a member of an xscope object. */ auto xscp_s_ptr1 = mse::make_xscope_pointer_to_member_v2((&a_scpobj), &A::s); (*xscp_s_ptr1) = "some new text"; auto xscp_s_const_ptr1 = mse::make_xscope_const_pointer_to_member_v2((&a_scpobj), &A::s); /* The return type of mse::make_xscope_pointer_to_member_v2() depends on the type of the parameters passed to it. In this case, the type of xscp_s_ptr1 is mse::TXScopeFixedPointer<A>. */ auto res5 = H::foo6(xscp_s_ptr1, xscp_s_const_ptr1); { /* Using mse::make_xscope_strong_pointer_store(), you can obtain a scope pointer from a refcounting pointer (or any other (copiable) "strong" pointer). */ /* Let's make it a const refcounting pointer, just for variety. */ mse::TRefCountingFixedConstPointer<A> refc_cptr1 = mse::make_refcounting<A>(11); auto xscp_refc_cstore = mse::make_xscope_strong_pointer_store(refc_cptr1); auto xscp_cptr1 = xscp_refc_cstore.xscope_ptr(); int res6 = B::foo3(xscp_cptr1); mse::TXScopeFixedConstPointer<A> xscp_cptr2 = xscp_cptr1; A res7 = *xscp_cptr2; } { /* For "strong" pointers that are not copiable (like TSingleOwnerPointer<>), you can use mse::make_xscope_borrowing_strong_pointer_store() to obtain a scope pointer. */ mse::TSingleOwnerPointer<A> so_ptr1 = mse::make_single_owner<A>(17); auto xscp_so_store = mse::make_xscope_borrowing_strong_pointer_store(&so_ptr1); auto xscp_ptr3 = xscp_so_store.xscope_ptr(); int res8 = B::foo3(xscp_ptr3); mse::TXScopeFixedConstPointer<A> xscp_ptr4 = xscp_ptr3; A res9 = *xscp_ptr4; } /* For safety reasons, non-owning scope pointers (or any objects containing a scope reference) are not permitted to be used as function return values. (The return_value() function wrapper enforces this.) Pretty much the only time you'd legitimately want to do this is when the returned pointer is one of the input parameters. An example might be a "min(a, b)" function which takes two objects by reference and returns the reference to the lesser of the two objects. For these cases you could use the xscope_chosen() function which takes two objects of the same type (in this case it will be two scope pointers) and returns one of the objects (scope pointers), which one depending on the value of a given "decider" function. You could use this function to implement the equivalent of a min(a, b) function like so: */ auto xscp_a_ptr5 = &a_scpobj; auto xscp_a_ptr6 = &(*xscp_a_ownerptr); const auto second_arg_is_smaller_fn = [](const auto xscp_a_ptr1, const auto xscp_a_ptr2) { return (*xscp_a_ptr2) < (*xscp_a_ptr1); }; auto xscp_min_ptr1 = mse::xscope_chosen(second_arg_is_smaller_fn, xscp_a_ptr5, xscp_a_ptr6); assert(5 == xscp_min_ptr1->b); { /***********************************/ /* rsv::as_a_returnable_fparam() */ /***********************************/ /* Another alternative if you want to return a scope pointer (or any object containing a scope reference) function parameter is to (immediately) create a "returnable" version of it using the rsv::as_a_returnable_fparam() function. Normally the return_value() function wrapper will reject (with a compile error) scope pointers as unsafe return values. But the rsv::as_a_returnable_fparam() function can be used to (immediately) obtain a "returnable" version of a scope pointer function parameter. Because it's generally safe to return a reference to an object if that reference was passed as a parameter. Well, as long as the object is not a temporary object. So unlike rsv::as_an_fparam(), rsv::as_a_returnable_fparam() will not accept scope pointers to temporaries, as returning a (scope) reference to a temporary would be unsafe even if the reference was passed as a function parameter. So for scope reference parameters you have to choose between being able to use it as a return value, or supporting references to temporaries. (Or neither.) Note that using this function on anything other than function parameters is unsafe, and currently there is no compile-time enforcement of this restriction. */ mse::TXScopeObj<mse::nii_string> xscope_string1 = "abc"; mse::TXScopeObj<mse::nii_string> xscope_string2 = "abcd"; auto longer_string_xscpptr = H::longest(&xscope_string1, &xscope_string2); auto length1 = (*longer_string_xscpptr).length(); auto longer_string_xscpptr2 = H::nested_longest(&xscope_string1, &xscope_string2); auto length2 = (*longer_string_xscpptr2).length(); mse::TXScopeObj<H::CE> e_xscpobj; auto xscope_string_const_section1 = H::xscope_string_const_section_to_member_of_CE(&e_xscpobj); assert(xscope_string_const_section1 == "bcd"); auto xscope_string_const_section2 = H::nested_xscope_string_const_section_to_member_of_CE(&e_xscpobj); assert(xscope_string_const_section2 == "bcd"); } { /****************************/ /* rsv::TFParam<> */ /* && rsv::as_an_fparam() */ /****************************/ /* rsv::TFParam<> is just a transparent template wrapper for function parameter declarations. In most cases use of this wrapper is not necessary, but in some cases it enables functionality only available to variables that are function parameters. Specifically, it allows functions to support arguments that are scope pointer/references to temporary objects. For safety reasons, by default, scope pointer/references to temporaries are actually "functionally disabled" types distinct from regular scope pointer/reference types. Because it's safe to do so in the case of function parameters, the rsv::TFParam<> wrapper enables certain scope pointer/reference types (like TXScopeFixedPointer<>, and "random access section" scope types) to be constructed from their "functionally disabled" counterparts. In the case of function templates, sometimes you want the parameter types to be auto-deduced, and use of the mse::rsv::TFParam<> wrapper can interfere with that. In those cases you can instead convert parameters to their wrapped type after-the-fact using the rsv::as_an_fparam() function. Note that using this function (or the rsv::TFParam<> wrapper) on anything other than function parameters is unsafe, and currently there is no compile-time enforcement of this restriction. rsv::TXScopeFParam<> and rsv::xscope_as_an_fparam() can be used for situations when the types are necessarily scope types. */ class CD { public: static bool second_is_longer(const mse::rsv::TXScopeFParam<mse::TXScopeFixedConstPointer<mse::nii_string> > string1_xscpptr , const mse::rsv::TXScopeFParam<mse::TXScopeFixedConstPointer<mse::nii_string> > string2_xscpptr) { return (string1_xscpptr->length() > string2_xscpptr->length()) ? false : true; } static bool second_is_longer_any(const mse::rsv::TXScopeFParam<mse::TXScopeAnyConstPointer<mse::nii_string> > string1_xscpptr , const mse::rsv::TXScopeFParam<mse::TXScopeAnyConstPointer<mse::nii_string> > string2_xscpptr) { return (string1_xscpptr->length() > string2_xscpptr->length()) ? false : true; } static bool second_is_longer_poly(const mse::rsv::TXScopeFParam<mse::TXScopePolyConstPointer<mse::nii_string> > string1_xscpptr , mse::rsv::TXScopeFParam<mse::TXScopePolyConstPointer<mse::nii_string> > string2_xscpptr) { return (string1_xscpptr->length() > string2_xscpptr->length()) ? false : true; } }; mse::TXScopeObj<mse::nii_string> xscope_string1 = "abc"; /* Here we're using the pointer_to() function to obtain a ("caged") pointer to the temporary scope object. The '&' (ampersand) operator would also work, but would not correspond to valid native C++, as C++ does not support taking the address of an r-value. */ auto res1 = CD::second_is_longer(&xscope_string1, mse::pointer_to(mse::TXScopeObj<mse::nii_string>(xscope_string1 + "de"))); auto res2 = H::second_is_longer(&xscope_string1, mse::pointer_to(mse::TXScopeObj<mse::nii_string>(xscope_string1 + "de"))); auto res3 = CD::second_is_longer_any(&xscope_string1, mse::pointer_to(mse::TXScopeObj<mse::nii_string>(xscope_string1 + "de"))); auto res4 = CD::second_is_longer_poly(&xscope_string1, mse::pointer_to(mse::TXScopeObj<mse::nii_string>(xscope_string1 + "de"))); } { /*******************************/ /* TRegisteredProxyPointer */ /*******************************/ /* Scope pointers (currently) have limitations, for example, in terms of their ability to be retargeted, and their ability to be stored in dynamic containers. When necessary, you can circumvent these sorts of limitations by creating "registered proxy" pointers corresponding to given scope pointers. Registered proxy pointers are basically just registered pointers which target scope pointers, except that (more conveniently) they dereference to the scope pointer's target object rather than the scope pointer itself. That is, a `TRegisteredProxyPointer<T>` is similar to a `TRegisteredPointer<TXScopeFixedPointer<T> >`, except that it dereferences to the object of type `T` rather than the `TXScopeFixedPointer<T>`. They are also convertible back to scope pointers when needed. To be clear, a `TRegisteredProxyPointer<T>` doesn't have any functionality that a `TRegisteredPointer<TXScopeFixedPointer<T> >` does not already have, it's just more convenient in some situations. */ class CB { public: static void foo1(mse::TXScopeFixedPointer<mse::nii_string> xscope_ptr1) { std::cout << *xscope_ptr1; } }; auto xscp_nstring1 = mse::make_xscope(mse::nii_string("some text")); CB::foo1(&xscp_nstring1); { auto xscp_proxy_obj1 = mse::make_xscope_registered_proxy(&xscp_nstring1); mse::TRegisteredProxyPointer<mse::nii_string> registered_proxy_ptr1 = mse::registered_proxy_fptr(xscp_proxy_obj1); /* Registered proxy pointers implicitly convert to scope pointers. */ CB::foo1(registered_proxy_ptr1); auto xscp_nstring2 = mse::make_xscope(mse::nii_string("some other text")); auto xscp_proxy_obj2 = mse::make_xscope_registered_proxy(&xscp_nstring2); /* Registered proxy pointers are retargetable. */ registered_proxy_ptr1 = mse::registered_proxy_fptr(xscp_proxy_obj2); CB::foo1(registered_proxy_ptr1); { auto xscp_nstring3 = mse::make_xscope(mse::nii_string("other text")); { auto xscp_proxy_obj3 = mse::make_xscope_registered_proxy(&xscp_nstring3); { registered_proxy_ptr1 = mse::registered_proxy_fptr(xscp_proxy_obj3); } CB::foo1(registered_proxy_ptr1); } /* Attempting to dereference registered_proxy_ptr1 here would result in an exception. */ //*registered_proxy_ptr1; } } #ifdef MSE_HAS_CXX17 { /* With C++17 and later, the fact that registered proxy pointers implicitly convert to scope pointers means that iterators and "random access sections" (including string sections) based on registered proxy pointers also implicitly convert to the corresponding iterators and random access sections based on scope pointers. */ auto xscp_proxy_obj1 = mse::make_xscope_registered_proxy(&xscp_nstring1); auto xscope_ptr1 = &xscp_nstring1; mse::TXScopeFixedConstPointer<mse::nii_string> xscope_cptr1 = &xscp_nstring1; auto proxy_ptr1 = mse::registered_proxy_fptr(xscp_proxy_obj1); auto xscope_string_section1 = mse::make_xscope_string_section(xscope_ptr1); auto xscope_string_const_section1 = mse::make_xscope_string_const_section(xscope_ptr1); auto proxy_string_section1 = mse::make_string_section(proxy_ptr1); /* Here a string section based on a registered proxy pointer is implicitly converting to a string section based on a scope pointer. */ decltype(xscope_string_section1) xscope_string_section2 = proxy_string_section1; decltype(xscope_string_const_section1) xscope_string_const_section2 = proxy_string_section1; assert(xscp_nstring1[0] == xscope_string_section2[0]); assert(xscp_nstring1[1] == xscope_string_const_section2[1]); } #endif /* MSE_HAS_CXX17 */ mse::self_test::CRegProxyPtrTest1::s_test1(); } { /**************************/ /* TNoradProxyPointer */ /**************************/ /* "norad proxy" pointers are to "registered proxy" pointers as norad pointers are to registered pointers. That is, the difference is that the destruction of a a norad proxy object while a norad proxy pointer still references it will result in program termination. So like their registered counterparts: Norad proxy pointers are basically just norad pointers which target scope pointers, except that (more conveniently) they dereference to the scope pointer's target object rather than the scope pointer itself. That is, a `TNoradProxyPointer<T>` is similar to a `TNoradPointer<TXScopeFixedPointer<T> >`, except that it dereferences to the object of type `T` rather than the `TXScopeFixedPointer<T>`. They are also convertible back to scope pointers when needed. To be clear, a `TNoradProxyPointer<T>` doesn't have any functionality that a `TNoradPointer<TXScopeFixedPointer<T> >` does not already have, it's just more convenient in some situations. */ class CB { public: static void foo1(mse::TXScopeFixedPointer<mse::nii_string> xscope_ptr1) { std::cout << *xscope_ptr1; } }; auto xscp_nstring1 = mse::make_xscope(mse::nii_string("some text")); CB::foo1(&xscp_nstring1); { auto xscp_proxy_obj1 = mse::make_xscope_norad_proxy(&xscp_nstring1); mse::TNoradProxyPointer<mse::nii_string> norad_proxy_ptr1 = mse::norad_proxy_fptr(xscp_proxy_obj1); /* Norad proxy pointers implicitly convert to scope pointers. */ CB::foo1(norad_proxy_ptr1); auto xscp_nstring2 = mse::make_xscope(mse::nii_string("some other text")); auto xscp_proxy_obj2 = mse::make_xscope_norad_proxy(&xscp_nstring2); /* Norad proxy pointers are retargetable. */ norad_proxy_ptr1 = mse::norad_proxy_fptr(xscp_proxy_obj2); CB::foo1(norad_proxy_ptr1); { auto xscp_nstring3 = mse::make_xscope(mse::nii_string("other text")); { auto xscp_proxy_obj3 = mse::make_xscope_norad_proxy(&xscp_nstring3); { norad_proxy_ptr1 = mse::norad_proxy_fptr(xscp_proxy_obj3); } CB::foo1(norad_proxy_ptr1); /* Forgetting to detarget norad_proxy pointers before their target object is destroyed (in this case, by going out of scope) would result in program termination. */ norad_proxy_ptr1 = nullptr; } } } #ifdef MSE_HAS_CXX17 { /* With C++17 and later, the fact that norad proxy pointers implicitly convert to scope pointers means that iterators and "random access sections" (including string sections) based on norad proxy pointers also implicitly convert to the corresponding iterators and random access sections based on scope pointers. */ auto xscp_proxy_obj1 = mse::make_xscope_norad_proxy(&xscp_nstring1); auto xscope_ptr1 = &xscp_nstring1; mse::TXScopeFixedConstPointer<mse::nii_string> xscope_cptr1 = &xscp_nstring1; auto proxy_ptr1 = mse::norad_proxy_fptr(xscp_proxy_obj1); auto xscope_string_section1 = mse::make_xscope_string_section(xscope_ptr1); auto xscope_string_const_section1 = mse::make_xscope_string_const_section(xscope_ptr1); auto proxy_string_section1 = mse::make_string_section(proxy_ptr1); /* Here a string section based on a norad proxy pointer is implicitly converting to a string section based on a scope pointer. */ decltype(xscope_string_section1) xscope_string_section2 = proxy_string_section1; decltype(xscope_string_const_section1) xscope_string_const_section2 = proxy_string_section1; assert(xscp_nstring1[0] == xscope_string_section2[0]); assert(xscp_nstring1[1] == xscope_string_const_section2[1]); } #endif /* MSE_HAS_CXX17 */ mse::self_test::CNoradProxyPtrTest1::s_test1(); } mse::self_test::CXScpPtrTest1::s_test1(); } { /*********************************/ /* make_pointer_to_member_v2() */ /*********************************/ /* If you have a safe pointer to an object, you can get a safe pointer to a member of that object using the make_pointer_to_member_v2() function. */ /* To demonstrate, first we'll declare some objects such that we can obtain safe pointers to those objects. For better or worse, this library provides a bunch of different safe pointers types. */ mse::TXScopeObj<H> h_scpobj; auto h_refcptr = mse::make_refcounting<H>(); mse::TRegisteredObj<H> h_regobj; mse::TCRegisteredObj<H> h_rlxregobj; /* Safe iterators are a type of safe pointer too. */ mse::mstd::vector<H> h_mstdvec; h_mstdvec.resize(1); auto h_mstdvec_iter = h_mstdvec.begin(); mse::us::msevector<H> h_msevec; h_msevec.resize(1); auto h_msevec_ipointer = h_msevec.ibegin(); auto h_msevec_ssiter = h_msevec.ss_begin(); /* And don't forget the safe async sharing pointers. */ auto h_access_requester = mse::make_asyncsharedv2readwrite<ShareableH>(); auto h_writelock_ptr = h_access_requester.writelock_ptr(); auto h_shared_immutable_ptr = mse::make_asyncsharedv2immutable<ShareableH>(); { /* So here's how you get a safe pointer to a member of the object using mse::make_pointer_to_member_v2(). */ auto h_string1_scpptr = mse::make_xscope_pointer_to_member_v2(&h_scpobj, &H::m_string1); (*h_string1_scpptr) = "some new text"; auto h_string1_scp_const_ptr = mse::make_xscope_const_pointer_to_member_v2(&h_scpobj, &H::m_string1); auto h_string1_refcptr = mse::make_pointer_to_member_v2(h_refcptr, &H::m_string1); (*h_string1_refcptr) = "some new text"; auto h_string1_regptr = mse::make_pointer_to_member_v2(&h_regobj, &H::m_string1); (*h_string1_regptr) = "some new text"; auto h_string1_rlxregptr = mse::make_pointer_to_member_v2(&h_rlxregobj, &H::m_string1); (*h_string1_rlxregptr) = "some new text"; auto h_string1_mstdvec_iter = mse::make_pointer_to_member_v2(h_mstdvec_iter, &H::m_string1); (*h_string1_mstdvec_iter) = "some new text"; auto h_string1_msevec_ipointer = mse::make_pointer_to_member_v2(h_msevec_ipointer, &H::m_string1); (*h_string1_msevec_ipointer) = "some new text"; auto h_string1_msevec_ssiter = mse::make_pointer_to_member_v2(h_msevec_ssiter, &H::m_string1); (*h_string1_msevec_ssiter) = "some new text"; auto h_string1_writelock_ptr = mse::make_pointer_to_member_v2(h_writelock_ptr, &H::m_string1); (*h_string1_writelock_ptr) = "some new text"; auto h_string1_stdshared_const_ptr = mse::make_const_pointer_to_member_v2(h_shared_immutable_ptr, &H::m_string1); //(*h_string1_stdshared_const_ptr) = "some new text"; } } MSE_SUPPRESS_CHECK_IN_XSCOPE { /*************************/ /* Simple Benchmarks */ /*************************/ /* Just some simple speed tests. */ class CE { public: CE() {} CE(int& count_ref) : m_count_ptr(&count_ref) { (*m_count_ptr) += 1; } virtual ~CE() { (*m_count_ptr) -= 1; } void increment() { (*m_count_ptr) += 1; } void decrement() { (*m_count_ptr) -= 1; } int m_x; int *m_count_ptr; }; #ifndef NDEBUG static const int number_of_loops = 10/*arbitrary*/; #else // !NDEBUG static const int number_of_loops = 10000000/*arbitrary*/; #endif // !NDEBUG std::cout << std::endl; std::cout << "Some simple benchmarks: \n"; std::cout << "number of loops: " << number_of_loops << " \n" << " \n"; { std::cout << "pointer declaration, copy and assignment: \n"; { int count = 0; CE object1(count); CE object2(count); CE* item_ptr2 = &object1; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { CE* item_ptr = (0 == (i % 2)) ? &object1 : &object2; item_ptr2 = item_ptr; (*item_ptr).increment(); (*item_ptr2).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "native pointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; mse::TNoradObj<CE> object1(count); mse::TNoradObj<CE> object2(count); mse::TNoradPointer<CE> item_ptr2 = &object1; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { mse::TNoradPointer<CE> item_ptr = (0 == (i % 2)) ? &object1 : &object2; item_ptr2 = item_ptr; (*item_ptr).increment(); (*item_ptr2).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TNoradPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; mse::TRefCountingPointer<CE> object1_ptr = mse::make_refcounting<CE>(count); mse::TRefCountingPointer<CE> object2_ptr = mse::make_refcounting<CE>(count); mse::TRefCountingPointer<CE> item_ptr2 = object1_ptr; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { mse::TRefCountingPointer<CE> item_ptr = (0 == (i % 2)) ? object1_ptr : object2_ptr; item_ptr2 = item_ptr; (*item_ptr).increment(); (*item_ptr2).decrement(); } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TRefCountingPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto object1_ptr = std::make_shared<CE>(count); auto object2_ptr = std::make_shared<CE>(count); auto item_ptr2 = object1_ptr; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { auto item_ptr = (0 == (i % 2)) ? object1_ptr : object2_ptr; item_ptr2 = item_ptr; (*item_ptr).increment(); (*item_ptr2).decrement(); } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "std::shared_ptr: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; mse::TRegisteredObj<CE> object1(count); mse::TRegisteredObj<CE> object2(count); mse::TRegisteredPointer<CE> item_ptr2 = &object1; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { mse::TRegisteredPointer<CE> item_ptr = (0 == (i % 2)) ? &object1 : &object2; item_ptr2 = item_ptr; (*item_ptr).increment(); (*item_ptr2).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TRegisteredPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; mse::TCRegisteredObj<CE> object1(count); mse::TCRegisteredObj<CE> object2(count); mse::TCRegisteredPointer<CE> item_ptr2 = &object1; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { mse::TCRegisteredPointer<CE> item_ptr = (0 == (i % 2)) ? &object1 : &object2; item_ptr2 = item_ptr; (*item_ptr).increment(); (*item_ptr2).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TCRegisteredPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } std::cout << std::endl; } { std::cout << "target object allocation and deallocation: \n"; { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { CE object(count); if (0 == (i % 2)) { object.increment(); } else { object.decrement(); } } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "native pointer targeting the stack: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { mse::TNoradObj<CE> object(count); if (0 == (i % 2)) { object.increment(); } else { object.decrement(); } } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TNoradPointer targeting the stack: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { mse::TRegisteredObj<CE> object(count); if (0 == (i % 2)) { object.increment(); } else { object.decrement(); } } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TRegisteredPointer targeting the stack: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); { for (int i = 0; i < number_of_loops; i += 1) { mse::TCRegisteredObj<CE> object(count); if (0 == (i % 2)) { object.increment(); } else { object.decrement(); } } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TCRegisteredPointer targeting the stack: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { auto owner_ptr = std::make_unique<CE>(count); if (0 == (i % 2)) { (*owner_ptr).increment(); } else { (*owner_ptr).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "native pointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { auto owner_ptr = std::make_unique<mse::TNoradObj<CE>>(count); if (0 == (i % 2)) { (*owner_ptr).increment(); } else { (*owner_ptr).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TNoradPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { mse::TRefCountingNotNullPointer<CE> owner_ptr = mse::make_refcounting<CE>(count); if (0 == (i % 2)) { (*owner_ptr).increment(); } else { (*owner_ptr).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TRefCountingPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { auto owner_ptr = std::make_shared<CE>(count); if (0 == (i % 2)) { (*owner_ptr).increment(); } else { (*owner_ptr).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "std::shared_ptr: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { auto owner_ptr = std::make_unique<mse::TRegisteredObj<CE>>(count); if (0 == (i % 2)) { (*owner_ptr).increment(); } else { (*owner_ptr).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TRegisteredPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } { int count = 0; auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number_of_loops; i += 1) { auto owner_ptr = std::make_unique<mse::TCRegisteredObj<CE>>(count); if (0 == (i % 2)) { (*owner_ptr).increment(); } else { (*owner_ptr).decrement(); } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TCRegisteredPointer: " << time_span.count() << " seconds."; if (0 != count) { std::cout << " destructions pending: " << count << "."; /* Using the count variable for (potential) output should prevent the optimizer from discarding it. */ } std::cout << std::endl; } std::cout << std::endl; } { std::cout << "dereferencing: \n"; static const int number_of_loops2 = (10/*arbitrary*/)*number_of_loops; { class CF { public: CF(int a = 0) : m_a(a) {} CF* m_next_item_ptr; int m_a = 3; }; CF item1(1); CF item2(2); CF item3(3); item1.m_next_item_ptr = &item2; item2.m_next_item_ptr = &item3; item3.m_next_item_ptr = &item1; auto t1 = std::chrono::high_resolution_clock::now(); CF* cf_ptr = item1.m_next_item_ptr; for (int i = 0; i < number_of_loops2; i += 1) { cf_ptr = cf_ptr->m_next_item_ptr; if (!cf_ptr) { #if __cpp_exceptions >= 199711 throw(""); #endif // __cpp_exceptions >= 199711 } } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "native pointer (checked) dereferencing: " << time_span.count() << " seconds."; if (3 == cf_ptr->m_a) { std::cout << " "; /* Using cf_ptr->m_a for (potential) output should prevent the optimizer from discarding too much. */ } std::cout << std::endl; } { class CF { public: CF(int a = 0) : m_a(a) {} CF* m_next_item_ptr; int m_a = 3; }; CF item1(1); CF item2(2); CF item3(3); item1.m_next_item_ptr = &item2; item2.m_next_item_ptr = &item3; item3.m_next_item_ptr = &item1; auto t1 = std::chrono::high_resolution_clock::now(); CF* cf_ptr = item1.m_next_item_ptr; for (int i = 0; i < number_of_loops2; i += 1) { cf_ptr = cf_ptr->m_next_item_ptr; } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "native pointer dereferencing: " << time_span.count() << " seconds."; if (3 == cf_ptr->m_a) { std::cout << " "; /* Using cf_ptr->m_a for (potential) output should prevent the optimizer from discarding too much. */ } std::cout << std::endl; } { class CF { public: CF(int a = 0) : m_a(a) {} mse::TNoradPointer<CF> m_next_item_ptr; int m_a = 3; }; mse::TNoradObj<CF> item1(1); mse::TNoradObj<CF> item2(2); mse::TNoradObj<CF> item3(3); item1.m_next_item_ptr = &item2; item2.m_next_item_ptr = &item3; item3.m_next_item_ptr = &item1; auto t1 = std::chrono::high_resolution_clock::now(); mse::TNoradPointer<CF>* rpfl_ptr = std::addressof(item1.m_next_item_ptr); for (int i = 0; i < number_of_loops2; i += 1) { rpfl_ptr = std::addressof((*rpfl_ptr)->m_next_item_ptr); } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TNoradPointer (checked) dereferencing: " << time_span.count() << " seconds."; if (3 == (*rpfl_ptr)->m_a) { std::cout << " "; /* Using rpfl_ref->m_a for (potential) output should prevent the optimizer from discarding too much. */ } std::cout << std::endl; item1.m_next_item_ptr = nullptr; item2.m_next_item_ptr = nullptr; item3.m_next_item_ptr = nullptr; } { class CF { public: CF(int a = 0) : m_a(a) {} mse::TCRegisteredPointer<CF> m_next_item_ptr; int m_a = 3; }; mse::TCRegisteredObj<CF> item1(1); mse::TCRegisteredObj<CF> item2(2); mse::TCRegisteredObj<CF> item3(3); item1.m_next_item_ptr = &item2; item2.m_next_item_ptr = &item3; item3.m_next_item_ptr = &item1; auto t1 = std::chrono::high_resolution_clock::now(); mse::TCRegisteredPointer<CF>* rpfl_ptr = std::addressof(item1.m_next_item_ptr); for (int i = 0; i < number_of_loops2; i += 1) { rpfl_ptr = std::addressof((*rpfl_ptr)->m_next_item_ptr); } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TCRegisteredPointer (checked) dereferencing: " << time_span.count() << " seconds."; if (3 == (*rpfl_ptr)->m_a) { std::cout << " "; /* Using rpfl_ref->m_a for (potential) output should prevent the optimizer from discarding too much. */ } std::cout << std::endl; } { class CF { public: CF(int a = 0) : m_a(a) {} mse::TCRegisteredPointer<CF> m_next_item_ptr; int m_a = 3; }; mse::TCRegisteredObj<CF> item1(1); mse::TCRegisteredObj<CF> item2(2); mse::TCRegisteredObj<CF> item3(3); item1.m_next_item_ptr = &item2; item2.m_next_item_ptr = &item3; item3.m_next_item_ptr = &item1; auto t1 = std::chrono::high_resolution_clock::now(); CF* cf_ptr = std::addressof(*(item1.m_next_item_ptr)); for (int i = 0; i < number_of_loops2; i += 1) { cf_ptr = std::addressof(*(cf_ptr->m_next_item_ptr)); } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TCRegisteredPointer unchecked dereferencing: " << time_span.count() << " seconds."; if (3 == cf_ptr->m_a) { std::cout << " "; /* Using rpfl_ref->m_a for (potential) output should prevent the optimizer from discarding too much. */ } std::cout << std::endl; } { class CF { public: CF(int a = 0) : m_a(a) {} mse::TRefCountingPointer<CF> m_next_item_ptr; int m_a = 3; }; auto item1_ptr = mse::make_refcounting<CF>(1); auto item2_ptr = mse::make_refcounting<CF>(2); auto item3_ptr = mse::make_refcounting<CF>(3); item1_ptr->m_next_item_ptr = item2_ptr; item2_ptr->m_next_item_ptr = item3_ptr; item3_ptr->m_next_item_ptr = item1_ptr; auto t1 = std::chrono::high_resolution_clock::now(); mse::TRefCountingPointer<CF>* refc_ptr = std::addressof(item1_ptr->m_next_item_ptr); for (int i = 0; i < number_of_loops2; i += 1) { refc_ptr = std::addressof((*refc_ptr)->m_next_item_ptr); } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "mse::TRefCountingPointer (checked) dereferencing: " << time_span.count() << " seconds."; item1_ptr->m_next_item_ptr = nullptr; /* to break the reference cycle */ if (3 == (*refc_ptr)->m_a) { std::cout << " "; /* Using refc_ref->m_a for (potential) output should prevent the optimizer from discarding too much. */ } std::cout << std::endl; } { class CF { public: CF(int a = 0) : m_a(a) {} std::weak_ptr<CF> m_next_item_ptr; int m_a = 3; }; auto item1_ptr = std::make_shared<CF>(1); auto item2_ptr = std::make_shared<CF>(2); auto item3_ptr = std::make_shared<CF>(3); item1_ptr->m_next_item_ptr = item2_ptr; item2_ptr->m_next_item_ptr = item3_ptr; item3_ptr->m_next_item_ptr = item1_ptr; auto t1 = std::chrono::high_resolution_clock::now(); std::weak_ptr<CF>* wp_ptr = &(item1_ptr->m_next_item_ptr); for (int i = 0; i < number_of_loops2; i += 1) { wp_ptr = &((*wp_ptr).lock()->m_next_item_ptr); } auto t2 = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); std::cout << "std::weak_ptr dereferencing: " << time_span.count() << " seconds."; if (3 == (*wp_ptr).lock()->m_a) { std::cout << " "; /* Using wp_ref.lock()->m_a for (potential) output should prevent the optimizer from discarding too much. */ } std::cout << std::endl; } std::cout << std::endl; } } msetl_example2(); msetl_example3(); return 0; } #ifdef __clang__ #pragma clang diagnostic pop #else /*__clang__*/ #ifdef __GNUC__ #pragma GCC diagnostic pop #endif /*__GNUC__*/ #endif /*__clang__*/ #ifdef _MSC_VER #pragma warning( pop ) #endif /*_MSC_VER*/ #else // !EXCLUDE_MSETL_EXAMPLE int main(int /*argc*/, char* /*argv*/[]) { msetl_example2(); msetl_example3(); return 0; } #endif // !EXCLUDE_MSETL_EXAMPLE
c++ source #4
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.44 VS17.14
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)
clad v2.2 (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.44 VS17.14
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 g++ 1.27
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.44 VS17.14
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 P2561)
x86-64 clang (experimental P2998)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3334)
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 (C++26 contracts + GNU extensions)
x86-64 gcc (C++26 contracts)
x86-64 gcc (C++26 reflection)
x86-64 gcc (P2034 lambdas)
x86-64 gcc (Thomas Healy)
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.3.0
x86-64 icx 2025.3.1
x86-64 icx 2025.3.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.15.2
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
// Copyright (c) 2015 Noah Lopez // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef EXCLUDE_MSETL_EXAMPLE3 #include "msetl_example_defs.h" #include "msetl_example3.h" #include "mseasyncshared.h" #include "msepoly.h" #include "msemsearray.h" #include "msemsevector.h" #include "msemsestring.h" #include "msestaticimmutable.h" #include "msescopeatomic.h" #include "mselegacyhelpers.h" /* not used here, but we include it in multiple files to catch any "multiple definition" linker errors. */ #include <list> #include <iostream> #include <ctime> #include <ratio> #include <chrono> #include <sstream> #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4100 4456 4189 4702 ) #endif /*_MSC_VER*/ #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-braces" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wunused-variable" #pragma clang diagnostic ignored "-Wunused-function" #pragma clang diagnostic ignored "-Wunused-local-typedefs" #pragma clang diagnostic ignored "-Wunused-but-set-variable" #else /*__clang__*/ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #endif /*__GNUC__*/ #endif /*__clang__*/ class K { public: template<class _TAsyncSharedReadWriteAccessRequester> static double foo7(_TAsyncSharedReadWriteAccessRequester A_ashar) { auto t1 = std::chrono::high_resolution_clock::now(); /* A_ashar.readlock_ptr() will block until it can obtain a read lock. */ auto ptr1 = A_ashar.readlock_ptr(); // while ptr1 exists it holds a (read) lock on the shared object auto t2 = std::chrono::high_resolution_clock::now(); std::this_thread::sleep_for(std::chrono::seconds(1)); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); auto timespan_in_seconds = time_span.count(); auto thread_id = std::this_thread::get_id(); //std::cout << "thread_id: " << thread_id << ", time to acquire read pointer: " << timespan_in_seconds << " seconds."; //std::cout << std::endl; return timespan_in_seconds; } /* This function takes a "random access section" (which is like an "array_view" or gsl::span) as its parameter. */ template<class _TStringRASection> static void foo8(_TStringRASection ra_section) { size_t delay_in_milliseconds = 3000/*arbitrary*/; if (1 <= ra_section.size()) { delay_in_milliseconds /= mse::as_a_size_t(ra_section.size()); } for (size_t i = 0; i < ra_section.size(); i += 1) { auto now1 = std::chrono::system_clock::now(); auto tt = std::chrono::system_clock::to_time_t(now1); /* Just trying to obtain a string with the current time and date. The standard library doesn't yet seem to provide a safe, portable way to do this. */ #ifdef _MSC_VER static const size_t buffer_size = 64; char buffer[buffer_size]; buffer[0] = '\0'; ctime_s(buffer, buffer_size, &tt); #else /*_MSC_VER*/ const auto buffer = ctime(&tt); #endif /*_MSC_VER*/ std::string now_str(buffer); ra_section[i] = now_str; std::this_thread::sleep_for(std::chrono::milliseconds(delay_in_milliseconds)); } } /* This function just obtains a writelock_ra_section from the given "splitter access requester" and calls the given function with the writelock_ra_section as the first argument. */ template<class TAsyncSplitterRASectionReadWriteAccessRequester, class TFunction, class... Args> static void invoke_with_writelock_ra_section1(TAsyncSplitterRASectionReadWriteAccessRequester ar, const TFunction function1, Args&&... args) { function1(ar.writelock_ra_section(), args...); } /* This function just obtains an xscope_random_access_section from the given access controlled pointer and calls the given function with the xscope_random_access_section as the first argument. */ template<class Ty, class TFunction, class... Args> static void invoke_with_ra_section(mse::TXScopeExclusiveStrongPointerStoreForAccessControl<Ty> xs_ac_store, const TFunction function1, Args&&... args) { auto xscope_ra_section = mse::make_xscope_random_access_section(xs_ac_store.xscope_pointer()); function1(xscope_ra_section, args...); } template<class _TAPointer> static void foo17(_TAPointer a_ptr) { { auto now1 = std::chrono::system_clock::now(); auto tt = std::chrono::system_clock::to_time_t(now1); /* Just trying to obtain a string with the current time and date. The standard library doesn't yet seem to provide a safe, portable way to do this. */ #ifdef _MSC_VER static const size_t buffer_size = 64; char buffer[buffer_size]; buffer[0] = '\0'; ctime_s(buffer, buffer_size, &tt); #else /*_MSC_VER*/ const auto buffer = ctime(&tt); #endif /*_MSC_VER*/ std::string now_str(buffer); a_ptr->s = now_str; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } template<class _TAPointer> static void foo17b(_TAPointer a_ptr) { static std::atomic<int> s_count(0); s_count += 1; a_ptr->s = std::to_string(s_count); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } template<class _TConstPointer, class _TPointer> static void foo18(_TConstPointer src_ptr, _TPointer dst_ptr) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); dst_ptr->s = src_ptr->s; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }; void msetl_example3() { { /********************/ /* TAsyncSharedV2 */ /********************/ /* The TAsyncSharedV2 data types are used to safely share objects between asynchronous threads. */ class A { public: A(int x) : b(x) {} virtual ~A() {} int b = 3; mse::nii_string s = "some text "; }; /* User-defined classes need to be declared as (safely) shareable in order to be accepted by the access requesters. */ typedef mse::rsv::TAsyncShareableAndPassableObj<A> ShareableA; /* trivially copyable class */ class D { public: D(int x) : b(x) {} int b = 3; }; /* User-defined classes need to be declared as (safely) shareable in order to be used with the atomic templates. */ typedef mse::rsv::TAsyncShareableAndPassableObj<D> ShareableD; class B { public: static double foo1(mse::TAsyncSharedV2ReadWriteAccessRequester<ShareableA> A_ashar) { auto t1 = std::chrono::high_resolution_clock::now(); /* mse::TAsyncSharedV2ReadWriteAccessRequester<ShareableA>::writelock_ptr() will block until it can obtain a write lock. */ auto ptr1 = A_ashar.writelock_ptr(); // while ptr1 exists it holds a (write) lock on the shared object auto t2 = std::chrono::high_resolution_clock::now(); std::this_thread::sleep_for(std::chrono::seconds(1)); auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); auto timespan_in_seconds = time_span.count(); auto thread_id = std::this_thread::get_id(); //std::cout << "thread_id: " << thread_id << ", time to acquire write pointer: " << timespan_in_seconds << " seconds."; //std::cout << std::endl; ptr1->s = std::to_string(timespan_in_seconds); return timespan_in_seconds; } static int foo2(mse::TAsyncSharedV2ImmutableFixedPointer<ShareableA> A_immptr) { return A_immptr->b; } static int foo3(mse::TAsyncSharedV2AtomicFixedPointer<ShareableD> D_atomic_ptr) { auto d = (*D_atomic_ptr).load(); d.b += 1; (*D_atomic_ptr).store(d); std::this_thread::sleep_for(std::chrono::milliseconds(10)); return (*D_atomic_ptr).load().b; } static int foo4(mse::TXScopeAtomicFixedPointer<ShareableD> xs_D_atomic_ptr) { auto d = (*xs_D_atomic_ptr).load(); d.b += 1; (*xs_D_atomic_ptr).store(d); std::this_thread::sleep_for(std::chrono::milliseconds(10)); return (*xs_D_atomic_ptr).load().b; } }; std::cout << std::endl; std::cout << "AsyncSharedV2 test output:"; std::cout << std::endl; { /* This block contains a simple example demonstrating the use of mse::TAsyncSharedV2ReadWriteAccessRequester to safely share an object between threads. */ std::cout << "TAsyncSharedV2ReadWriteAccessRequester:"; std::cout << std::endl; auto ash_access_requester = mse::make_asyncsharedv2readwrite<ShareableA>(7); ash_access_requester.writelock_ptr()->b = 11; int res1 = ash_access_requester.readlock_ptr()->b; { auto ptr3 = ash_access_requester.readlock_ptr(); auto ptr1 = ash_access_requester.writelock_ptr(); auto ptr2 = ash_access_requester.writelock_ptr(); } std::list<mse::mstd::future<double>> futures; for (size_t i = 0; i < 3; i += 1) { futures.emplace_back(mse::mstd::async(B::foo1, ash_access_requester)); } int count = 1; for (auto it = futures.begin(); futures.end() != it; it++, count++) { std::cout << "thread: " << count << ", time to acquire read pointer: " << (*it).get() << " seconds."; std::cout << std::endl; } std::cout << std::endl; } { std::cout << "TAsyncSharedV2ReadOnlyAccessRequester:"; std::cout << std::endl; auto ash_access_requester = mse::make_asyncsharedv2readonly<ShareableA>(7); int res1 = ash_access_requester.readlock_ptr()->b; std::list<mse::mstd::future<double>> futures; for (size_t i = 0; i < 3; i += 1) { futures.emplace_back(mse::mstd::async(K::foo7<mse::TAsyncSharedV2ReadOnlyAccessRequester<ShareableA>>, ash_access_requester)); } int count = 1; for (auto it = futures.begin(); futures.end() != it; it++, count++) { std::cout << "thread: " << count << ", time to acquire read pointer: " << (*it).get() << " seconds."; std::cout << std::endl; } std::cout << std::endl; } #ifndef EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1 { /* Just demonstrating the existence of the "try" versions. */ auto access_requester = mse::make_asyncsharedv2readwrite<mse::nii_string>("some text"); auto writelock_ptr1 = access_requester.try_writelock_ptr(); if (writelock_ptr1) { // lock request succeeded int q = 5; } auto readlock_ptr2 = access_requester.try_readlock_ptr_for(std::chrono::seconds(1)); auto writelock_ptr3 = access_requester.try_writelock_ptr_until(std::chrono::steady_clock::now() + std::chrono::seconds(1)); } #endif // !EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1 { /* TAsyncSharedV2WeakReadWriteAccessRequester<> is the weak counterpart to TAsyncSharedV2ReadWriteAccessRequester<> analogous to how std::weak_ptr<> is the weak counterpart to std::shared_ptr<>. */ typedef decltype(mse::make_asyncsharedv2readwrite<mse::nii_string>("abc")) access_requester_t; auto vec1 = mse::mstd::vector<access_requester_t>(); vec1.push_back(mse::make_asyncsharedv2readwrite<mse::nii_string>("abc")); mse::TAsyncSharedV2WeakReadWriteAccessRequester<mse::nii_string> weak_ar1(vec1.at(0)); /* Here we're obtaining a (strong) access requester from the weak access requester, then appending it the vector of access requesters. */ vec1.push_back(weak_ar1.try_strong_access_requester().value()); assert((*(vec1.at(1).readlock_ptr())) == "abc"); vec1.clear(); /* All the (strong) access requesters have just been destroyed so attempting to obtain a (strong) access requester from our weak one will result in an empty optional being returned. */ assert(!(weak_ar1.try_strong_access_requester().has_value())); } { /* For scenarios where the shared object is immutable (i.e. is never modified), you can get away without using locks or access requesters. */ auto A_immptr = mse::make_asyncsharedv2immutable<ShareableA>(5); int res1 = A_immptr->b; std::shared_ptr<const ShareableA> A_shptr(A_immptr); std::list<mse::mstd::future<int>> futures; for (size_t i = 0; i < 3; i += 1) { futures.emplace_back(mse::mstd::async(B::foo2, A_immptr)); } int count = 1; for (auto it = futures.begin(); futures.end() != it; it++, count++) { int res2 = (*it).get(); } auto A_b_safe_cptr = mse::make_const_pointer_to_member_v2(A_immptr, &A::b); } { /* For scenarios where the shared object is atomic, you can get away without using locks or access requesters. */ auto D_atomic_ptr = mse::make_asyncsharedv2atomic<ShareableD>(5); int res1 = (*D_atomic_ptr).load().b; std::list<mse::mstd::future<int>> futures; for (size_t i = 0; i < 3; i += 1) { futures.emplace_back(mse::mstd::async(B::foo3, D_atomic_ptr)); } int count = 1; for (auto it = futures.begin(); futures.end() != it; it++, count++) { int res2 = (*it).get(); } } { /* mse::TAsyncSharedV2ReadWriteAccessRequester's flexibilty in allowing coexisting read and write lock pointers in the same thread introduces new ways to produce logical deadlocks. This block (likely) demonstrates the access requester's ability to detect these potential deadlocks (and throw an exception when they would occur). */ std::cout << "TAsyncSharedV2ReadWriteAccessRequester deadlock detection:"; std::cout << std::endl; class CC { public: static void foo1(mse::TAsyncSharedV2ReadWriteAccessRequester<ShareableA> A_ashar, int id) { auto readlock_ptr = A_ashar.readlock_ptr(); std::this_thread::sleep_for(std::chrono::seconds(1)); MSE_TRY { auto writelock_ptr = A_ashar.writelock_ptr(); std::this_thread::sleep_for(std::chrono::seconds(1)); } MSE_CATCH_ANY { // likely exception due to potential deadlock std::cout << "deadlock detected "; std::cout << std::endl; } } }; auto ash_access_requester = mse::make_asyncsharedv2readwrite<ShareableA>(7); { auto thread1 = mse::mstd::thread(CC::foo1, ash_access_requester, 1); auto thread2 = mse::mstd::thread(CC::foo1, ash_access_requester, 2); thread1.join(); thread2.join(); } std::cout << std::endl; } { /* This block demonstrates safely allowing different threads to (simultaneously) modify different sections of a vector. (We use vectors in this example, but it works just as well with arrays.) */ MSE_DECLARE_STATIC_IMMUTABLE(size_t) num_sections = 10; MSE_DECLARE_STATIC_IMMUTABLE(size_t) section_size = 5; const size_t num_elements = size_t(num_sections * section_size); typedef mse::nii_vector<mse::nii_string> async_shareable_vector1_t; typedef mse::mstd::vector<mse::nii_string> nonshareable_vector1_t; /* Let's say we have a vector. */ nonshareable_vector1_t vector1; vector1.resize(num_elements); { size_t count = 0; for (auto iter = vector1.begin(); vector1.end() != iter; iter++) { count += 1; *iter = "text" + std::to_string(count); } } /* Only access controlled objects can be shared with other threads, so we'll make an access controlled vector and (temporarily) swap it with our original one. */ auto ash_access_requester = mse::make_asyncsharedv2readwrite<async_shareable_vector1_t>(); std::swap(vector1, (*(ash_access_requester.writelock_ptr()))); std::cout << "mse::TAsyncRASectionSplitter<>, part 1: " << std::endl; { /* Now, we're going to use the access requester to obtain two new access requesters that provide access to (newly created) "random access section" objects which are used to access (disjoint) sections of the vector. We need to specify the position where we want to split the vector. Here we specify that it be split at index "num_elements / 2", right down the middle. */ mse::TAsyncRASectionSplitter<decltype(ash_access_requester)> ra_section_split1(ash_access_requester, num_elements / 2); auto ar1 = ra_section_split1.first_ra_section_access_requester(); auto ar2 = ra_section_split1.second_ra_section_access_requester(); /* The K::foo8 template function is just an example function that operates on containers of strings. In our case the containers will be the random access sections we just created. We'll create an instance of the function here. */ const auto my_foo8_function = K::foo8<decltype(ar1.writelock_ra_section())>; /* We want to execute the my_foo8 function in a separate thread. The function takes a "random access section" as an argument. But as we're not allowed to pass random access sections between threads, we must pass an access requester instead. The "K::invoke_with_writelock_ra_section1" template function is just a helper function that will obtain a (writelock) random access section from the access requester, then call the given function, in this case my_foo8, with that random access section. So here we'll use it to create a proxy function that we can execute directly in a separate thread and will accept an access requester as a parameter. */ const auto my_foo8_proxy_function = K::invoke_with_writelock_ra_section1<decltype(ar1), decltype(my_foo8_function)>; std::list<mse::mstd::thread> threads; /* So this thread will modify the first section of the vector. */ threads.emplace_back(mse::mstd::thread(my_foo8_proxy_function, ar1, my_foo8_function)); /* While this thread modifies the other section. */ threads.emplace_back(mse::mstd::thread(my_foo8_proxy_function, ar2, my_foo8_function)); { int count = 1; for (auto it = threads.begin(); threads.end() != it; it++, count++) { (*it).join(); } } int q = 5; } std::cout << "mse::TAsyncRASectionSplitter<>, part 2: " << std::endl; { /* Ok, now let's do it again, but instead of splitting the vector into two sections, let's split it into more sections: */ /* First we create a list of a the sizes of each section. We'll use a vector here, but any iteratable container will work. */ mse::mstd::vector<size_t> section_sizes; for (size_t i = 0; i < num_sections; i += 1) { section_sizes.push_back(size_t(section_size)); } /* Just as before, TAsyncRASectionSplitter<> will generate a new access requester for each section. */ mse::TAsyncRASectionSplitter<decltype(ash_access_requester)> ra_section_split1(ash_access_requester, section_sizes); auto ar0 = ra_section_split1.ra_section_access_requester(0); const auto my_foo8_function = K::foo8<decltype(ar0.writelock_ra_section())>; const auto my_foo8_proxy_function = K::invoke_with_writelock_ra_section1<decltype(ar0), decltype(my_foo8_function)>; { /* Here we demonstrate scope threads. Scope threads don't support being copied or moved. Unlike mstd::thread, scope threads can share objects declared on the stack (which is not utilized here), and in their destructor, scope thread objects will wait until their thread finishes its execution (i.e "join" the thread), blocking if necessary. Often rather than declaring scope thread objects directly, you'll create and manage multiple scope threads with an "xscope_thread_carrier". An xscope_thread_carrier is just a container that holds scope threads. */ mse::xscope_thread_carrier xscope_threads; for (size_t i = 0; i < num_sections; i += 1) { auto ar = ra_section_split1.ra_section_access_requester(i); xscope_threads.new_thread(my_foo8_proxy_function, ar, my_foo8_function); } /* The scope will not end until all the scope threads have finished executing. */ } } /* Now that we're done sharing the (controlled access) vector, we can swap it back to our original vector. */ std::swap(vector1, (*(ash_access_requester.writelock_ptr()))); auto first_element_value = vector1[0]; auto last_element_value = vector1.back(); int q = 5; } { /* Here we demonstrate safely sharing an existing stack allocated object among threads. */ std::cout << "xscope_future_carrier<>: " << std::endl; /* (Mutable) objects can be shared between threads only if they are "access controlled". You can make an object "access controlled" by wrapping its type with the mse::TXScopeAccessControlledObj<> template wrapper. */ mse::TXScopeObj<mse::TXScopeAccessControlledObj<ShareableA> > a_xscpacobj(7); /* Here we obtain a scope access requester for the access controlled object. */ auto xscope_access_requester = mse::make_xscope_asyncsharedv2acoreadwrite(&a_xscpacobj); /* xscope_future_carrier<> is just a container that holds and manages scope futures. */ mse::xscope_future_carrier<double> xscope_futures; std::list<mse::xscope_future_carrier<double>::handle_t> future_handles; for (size_t i = 0; i < 3; i += 1) { /* You add a future by specifying the async() function and parameters that will return the future value. */ auto handle = xscope_futures.new_future(K::foo7<decltype(xscope_access_requester)>, xscope_access_requester); /* You need to store the handle of the added future in order to later retrieve its value. */ future_handles.emplace_back(handle); } int count = 1; for (auto it = future_handles.begin(); future_handles.end() != it; it++, count++) { std::cout << "thread: " << count << ", time to acquire read pointer: " << xscope_futures.xscope_ptr_at(*it)->get() << " seconds."; std::cout << std::endl; } std::cout << std::endl; } { mse::TXScopeObj<mse::TXScopeAccessControlledObj<ShareableA> > a_xscpacobj1(3); mse::TXScopeObj<mse::TXScopeAccessControlledObj<ShareableA> > a_xscpacobj2(5); mse::TXScopeObj<mse::TXScopeAccessControlledObj<ShareableA> > a_xscpacobj3(7); { std::cout << "mse::make_xscope_aco_locker_for_sharing(): " << std::endl; /* The mse::make_xscope_aco_locker_for_sharing() function takes a scope pointer to an "access controlled object" and returns a "locker" object which then holds an exclusive reference to the given access controlled object. From this locker object, you can obtain either one "scope passable" (non-const) pointer, or any number of "scope passable" const pointers. These scope passable pointers can then be safely passed directly as arguments to scope threads. This is a (little) more cumbersome, more restrictive way of sharing an object than, say, using the library's "access requesters". So generally using access requesters would be preferred. But you might choose to do it this way in certain cases where performance is critical. When using access requesters, each thread obtains the desired lock on a thread-safe mutex. Here we're obtaining the lock before launching the thread(s), so the mutex does not need to be thread-safe, thus saving a little overhead. */ auto xscope_aco_locker1 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj1); typedef decltype(xscope_aco_locker1.xscope_passable_pointer()) passable_exclusive_pointer_t; mse::xscope_thread xscp_thread1(K::foo17b<passable_exclusive_pointer_t>, xscope_aco_locker1.xscope_passable_pointer()); } { auto xscope_aco_locker1 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj1); auto xscope_aco_locker2 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj2); auto xscope_aco_locker3 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj3); typedef decltype(xscope_aco_locker1.xscope_passable_const_pointer()) passable_const_pointer_t; typedef decltype(xscope_aco_locker2.xscope_passable_pointer()) passable_exclusive_pointer_t; mse::xscope_thread xscp_thread1(K::foo18<passable_const_pointer_t, passable_exclusive_pointer_t> , xscope_aco_locker1.xscope_passable_const_pointer() , xscope_aco_locker2.xscope_passable_pointer()); mse::xscope_thread xscp_thread2(K::foo18<passable_const_pointer_t, passable_exclusive_pointer_t> , xscope_aco_locker1.xscope_passable_const_pointer() , xscope_aco_locker3.xscope_passable_pointer()); } { auto xscope_aco_locker1 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj1); /* The mse::make_xscope_exclusive_strong_pointer_store_for_sharing() function returns the same kind of "locker" object that mse::make_xscope_aco_locker_for_sharing() does, but instead of taking a scope pointer to an "access controlled object", it accepts any recognized "exclusive" pointer. That is, a pointer that, while it exists, holds exclusive access to its target object. */ auto xscope_strong_ptr_store1 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing(xscope_aco_locker1.xscope_passable_pointer()); auto xscope_aco_locker2 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj2); auto xscope_aco_locker3 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj3); typedef decltype(xscope_aco_locker1.xscope_passable_const_pointer()) passable_const_pointer_t; typedef decltype(xscope_aco_locker2.xscope_passable_pointer()) passable_exclusive_pointer_t; mse::xscope_thread xscp_thread1(K::foo18<passable_const_pointer_t, passable_exclusive_pointer_t> , xscope_strong_ptr_store1.xscope_passable_const_pointer() , xscope_aco_locker2.xscope_passable_pointer()); mse::xscope_thread xscp_thread2(K::foo18<passable_const_pointer_t, passable_exclusive_pointer_t> , xscope_strong_ptr_store1.xscope_passable_const_pointer() , xscope_aco_locker3.xscope_passable_pointer()); } { /* In this block we demonstrate obtaining various types of (const and non-const) pointers you might need from an exclusive pointer that might be passed to a thread. */ std::cout << "mse::TXScopeExclusiveStrongPointerStoreForAccessControl<>: " << std::endl; a_xscpacobj1.pointer()->s = ""; auto xscope_aco_locker1 = mse::make_xscope_aco_locker_for_sharing(&a_xscpacobj1); typedef decltype(xscope_aco_locker1.xscope_passable_pointer()) passable_exclusive_pointer_t; typedef decltype(xscope_aco_locker1.xscope_passable_const_pointer()) passable_const_pointer_t; class CD { public: /* mse::TXScopeExclusiveStrongPointerStoreForAccessControl<> is a data type that stores an exclusive strong pointer. From this data type you can obtain const, non-const and exclusive pointers. So this function expects to be passed an (rvlaue) pointer of type passable_exclusive_pointer_t. */ static void foo1(mse::TXScopeExclusiveStrongPointerStoreForAccessControl<passable_exclusive_pointer_t> xscope_store, int count) { { auto xsptr = xscope_store.xscope_pointer(); xsptr->s.append(std::to_string(count)); } { /* Here, from the exclusive (non-const) pointer passed to this function, we're going to obtain a couple of const pointers that we can pass to different (scope) threads. */ auto xscope_strong_ptr_store1 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing(xscope_store.xscope_exclusive_pointer()); mse::xscope_thread xscp_thread1(CD::foo2, xscope_strong_ptr_store1.xscope_passable_const_pointer()); mse::xscope_thread xscp_thread2(CD::foo2, xscope_strong_ptr_store1.xscope_passable_const_pointer()); } if (1 <= count) { /* And here we're going to (re)obtain an exclusive strong pointer like the one that was passed to this function, then we're going to use it to recursively call this function again in another (scope) thread. */ auto xscope_strong_ptr_store1 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing(xscope_store.xscope_exclusive_pointer()); mse::xscope_thread xscp_thread1(CD::foo1, xscope_strong_ptr_store1.xscope_passable_pointer(), count - 1); } } static void foo2(passable_const_pointer_t xscope_A_cptr) { std::cout << xscope_A_cptr->s << std::endl; } }; mse::xscope_thread xscp_thread1(CD::foo1, xscope_aco_locker1.xscope_passable_pointer(), 3); std::cout << std::endl; } } { /* TExclusiveWriterObj<> is a specialization of TAccessControlledObj<> for which all non-const pointers are exclusive. That is, when a non-const pointer exists, no other pointer may exist. */ mse::TXScopeObj<mse::TExclusiveWriterObj<ShareableA> > a_xscpxwobj1(3); mse::TXScopeObj<mse::TExclusiveWriterObj<ShareableA> > a_xscpxwobj2(5); mse::TXScopeObj<mse::TExclusiveWriterObj<ShareableA> > a_xscpxwobj3(7); { /* A (non-const) pointer of an "exclusive writer object" qualifies as an "exclusive strong" pointer, and thus you can obtain an xscope shareable pointer from it in the standard way. */ auto xscope_xwo_pointer_store1 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing(a_xscpxwobj1.pointer()); typedef decltype(xscope_xwo_pointer_store1.xscope_passable_pointer()) passable_exclusive_pointer_t; mse::xscope_thread xscp_thread1(K::foo17b<passable_exclusive_pointer_t>, xscope_xwo_pointer_store1.xscope_passable_pointer()); } { /* But uniquely, you can obtain an xscope shareable const pointer from a (non-exclusive) const pointer of an "exclusive writer object". There is a special function for this purpose: */ auto xscope_xwo_const_pointer_store1 = mse::make_xscope_exclusive_write_obj_const_pointer_store_for_sharing(a_xscpxwobj1.const_pointer()); auto xscope_xwo_pointer_store2 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing(a_xscpxwobj2.pointer()); auto xscope_xwo_pointer_store3 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing(a_xscpxwobj3.pointer()); typedef decltype(xscope_xwo_const_pointer_store1.xscope_passable_const_pointer()) passable_const_pointer_t; typedef decltype(xscope_xwo_pointer_store2.xscope_passable_pointer()) passable_exclusive_pointer_t; mse::xscope_thread xscp_thread1(K::foo18<passable_const_pointer_t, passable_exclusive_pointer_t> , xscope_xwo_const_pointer_store1.xscope_passable_const_pointer() , xscope_xwo_pointer_store2.xscope_passable_pointer()); mse::xscope_thread xscp_thread2(K::foo18<passable_const_pointer_t, passable_exclusive_pointer_t> , xscope_xwo_const_pointer_store1.xscope_passable_const_pointer() , xscope_xwo_pointer_store3.xscope_passable_pointer()); } } { /* For scenarios where the shared object is atomic, you can get away without using locks or access requesters. */ mse::TXScopeAtomicObj<ShareableD> xscope_D_atomic_obj(7); int res1 = xscope_D_atomic_obj.load().b; auto xscope_D_atomic_ptr = &xscope_D_atomic_obj; mse::xscope_thread xscp_thread1(B::foo4, xscope_D_atomic_ptr); mse::xscope_thread xscp_thread2(B::foo4, xscope_D_atomic_ptr); int res2 = (*xscope_D_atomic_ptr).load().b; } #ifndef EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1 { /* This block is similar to a previous one that demonstrates safely allowing different threads to (simultaneously) modify different sections of a vector. The difference is just that here the shared vector is a pre-existing one declared as a local variable. */ MSE_DECLARE_STATIC_IMMUTABLE(size_t) num_sections = 10; MSE_DECLARE_STATIC_IMMUTABLE(size_t) section_size = 5; const size_t num_elements = size_t(num_sections * section_size); typedef mse::nii_vector<mse::nii_string> async_shareable_vector1_t; typedef mse::mstd::vector<mse::nii_string> nonshareable_vector1_t; /* Let's say we have a vector. */ nonshareable_vector1_t vector1; vector1.resize(num_elements); { size_t count = 0; for (auto iter = vector1.begin(); vector1.end() != iter; iter++) { count += 1; *iter = "text" + std::to_string(count); } } /* Only access controlled objects can be shared with other threads, so we'll make an access controlled vector and (temporarily) swap it with our original one. */ mse::TXScopeObj<mse::TXScopeAccessControlledObj<async_shareable_vector1_t> > xscope_acobj; std::swap(vector1, *(xscope_acobj.xscope_pointer())); std::cout << "mse::TXScopeACORASectionSplitter<>: " << std::endl; { /* From the access controlled vector we're going to obtain scope pointers to two access controlled "random access section" objects which are used to access (disjoint) sections of the vector. Then we're going to use "locker" objects to obtain pointers that are designated as safe to pass to other threads. We need to specify the position where we want to split the vector. Here we specify that it be split at index "num_elements / 2", right down the middle. */ mse::TXScopeACORASectionSplitter<async_shareable_vector1_t> xscope_ra_section_split2(&xscope_acobj, num_elements / 2); /* Here we obtain scope pointers to the access controlled sections. */ auto first_ra_section_aco_xsptr = xscope_ra_section_split2.xscope_ptr_to_first_ra_section_aco(); auto second_ra_section_aco_xsptr = xscope_ra_section_split2.xscope_ptr_to_second_ra_section_aco(); /* Access controlled object pointers aren't themselves passable to other threads, but we can obtain corresponding pointers that are passable via a "locker" object that takes exclusive control over the access controlled object. */ #ifndef _MSC_VER auto xscope_section_access_owner1 = mse::make_xscope_aco_locker_for_sharing(first_ra_section_aco_xsptr); auto xscope_section_access_owner2 = mse::make_xscope_aco_locker_for_sharing(second_ra_section_aco_xsptr); #else // !_MSC_VER /* An apparent bug in msvc2017 and msvc2019preview (March 2019) prevents the other branch from compiling. This presents an opportunity to demonstrate an alternative solution. Instead of a "locker" object that takes a scope pointer to the access controlled object, we can use an "exclusive strong pointer store" which takes any recognized exclusive pointer. */ auto xscope_section_access_owner1 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing( (*first_ra_section_aco_xsptr).xscope_exclusive_pointer()); auto xscope_section_access_owner2 = mse::make_xscope_exclusive_strong_pointer_store_for_sharing( (*second_ra_section_aco_xsptr).xscope_exclusive_pointer()); #endif // !_MSC_VER /* The K::foo8 template function is just an example function that operates on containers of strings. In this case the containers will be the random access sections we just created. We'll create an instance of the function here. */ const auto my_foo8_function = K::foo8<mse::TXScopeAnyRandomAccessSection<mse::nii_string> >; typedef decltype(my_foo8_function) my_foo8_function_type; mse::xscope_thread_carrier threads; /* So this thread will modify the first section of the vector. */ threads.new_thread(K::invoke_with_ra_section<decltype(xscope_section_access_owner1.xscope_passable_pointer()), my_foo8_function_type> , xscope_section_access_owner1.xscope_passable_pointer(), my_foo8_function); /* While this thread modifies the other section. */ threads.new_thread(K::invoke_with_ra_section<decltype(xscope_section_access_owner2.xscope_passable_pointer()), my_foo8_function_type> , xscope_section_access_owner2.xscope_passable_pointer(), my_foo8_function); /* Note that in this particular scenario we didn't need to use any access requesters (or (thread safe) locks). */ } std::cout << "mse::TXScopeAsyncACORASectionSplitter<>: " << std::endl; { /* Ok, now let's do it again, but instead of splitting the vector into two sections, let's split it into more sections: */ /* First we create a list of a the sizes of each section. We'll use a vector here, but any iteratable container will work. */ mse::mstd::vector<size_t> section_sizes; for (size_t i = 0; i < num_sections; i += 1) { section_sizes.push_back(size_t(section_size)); } /* This time (for demonstration purposes) we'll use TXScopeAsyncACORASectionSplitter<> to generate a new access requester for each section. */ mse::TXScopeAsyncACORASectionSplitter<async_shareable_vector1_t> xscope_ra_section_split1(&xscope_acobj, section_sizes); auto ar0 = xscope_ra_section_split1.ra_section_access_requester(0); const auto my_foo8_function = K::foo8<decltype(ar0.writelock_ra_section())>; typedef decltype(my_foo8_function) my_foo8_function_type; const auto my_foo8_proxy_function = K::invoke_with_writelock_ra_section1<decltype(ar0), my_foo8_function_type>; { mse::xscope_thread_carrier xscope_threads; for (size_t i = 0; i < num_sections; i += 1) { auto ar = xscope_ra_section_split1.ra_section_access_requester(i); xscope_threads.new_thread(my_foo8_proxy_function, ar, my_foo8_function); } /* The scope will not end until all the scope threads have finished executing. */ } } /* Now that we're done sharing the (controlled access) vector, we can swap it back to our original vector. */ std::swap(vector1, *(xscope_acobj.xscope_pointer())); auto first_element_value = vector1[0]; auto last_element_value = vector1.back(); int q = 5; } #endif // !EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1 } } #ifdef __clang__ #pragma clang diagnostic pop #else /*__clang__*/ #ifdef __GNUC__ #pragma GCC diagnostic pop #endif /*__GNUC__*/ #endif /*__clang__*/ #ifdef _MSC_VER #pragma warning( pop ) #endif /*_MSC_VER*/ #else // !EXCLUDE_MSETL_EXAMPLE3 void msetl_example3() {} #endif // !EXCLUDE_MSETL_EXAMPLE3
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