Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
5bca42b049 | |||
e1ba7dd7a9 | |||
837f696971 | |||
afbfd0e781 | |||
0776012d09 | |||
4f8cbd442a | |||
2d309a2cff | |||
f19d93a69a | |||
e034efe8bd | |||
2c457c4275 | |||
95598e24e1 | |||
99c8ae9530 | |||
781877bd26 | |||
c692909ff3 | |||
a665c590d5 | |||
7023c0cc9a | |||
4884b59433 | |||
a7475b6af2 | |||
8b6f57dc6a | |||
d1bb5a83f6 | |||
4630cb5237 | |||
dce2c187ec | |||
cbade33906 | |||
c8b73031d8 | |||
af4e29f8c0 | |||
636882316b | |||
f0cccbe328 |
9
.bazelrc
9
.bazelrc
@ -1,11 +1,11 @@
|
||||
startup --windows_enable_symlinks
|
||||
build:windows --enable_runfiles=true
|
||||
|
||||
build --enable_platform_specific_config
|
||||
build --build_python_zip=false
|
||||
|
||||
build:windows --extra_execution_platforms=//:x64_windows-clang-cl
|
||||
# @Todo(bazel) We should be able to use @local_config_cc...
|
||||
build:windows --extra_toolchains=@@rules_cc++cc_configure_extension+local_config_cc//:cc-toolchain-x64_windows-clang-cl
|
||||
build:windows --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl
|
||||
|
||||
build:linux --repo_env=CC=clang
|
||||
|
||||
@ -24,14 +24,15 @@ build --cxxopt=-Wno-extra-semi
|
||||
build --cxxopt=-Wno-global-constructors
|
||||
build --cxxopt=-Wno-unsafe-buffer-usage
|
||||
build --cxxopt=-Wno-covered-switch-default
|
||||
build --cxxopt=-Wno-unused-command-line-argument
|
||||
|
||||
build:windows_san --config=windows
|
||||
build:windows_san --copt=-fno-sanitize-ignorelist
|
||||
build:windows_san --copt=-fsanitize=address
|
||||
build:windows_san --copt=-fsanitize=undefined
|
||||
build:windows_san --copt=-fno-sanitize-recover=all
|
||||
build:windows_san --linkopt=clang_rt.asan-x86_64.lib
|
||||
build:windows_san --copt=/MT
|
||||
build:windows_san --linkopt=clang_rt.asan_dynamic-x86_64.lib
|
||||
build:windows_san --linkopt=clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
|
||||
|
||||
build:linux_san --config=linux
|
||||
build:linux_san --copt=-fsanitize=address
|
||||
|
49
.clang-tidy
49
.clang-tidy
@ -1,34 +1,23 @@
|
||||
Checks:
|
||||
- "hicpp-*"
|
||||
- "cppcoreguidelines-*"
|
||||
- "misc-*"
|
||||
- "clang-analyzer-*"
|
||||
- "-misc-include-cleaner"
|
||||
- "performance-*"
|
||||
- "readability-*"
|
||||
- "-*-named-parameter"
|
||||
- "-*-avoid-do-while"
|
||||
- "-*-magic-numbers"
|
||||
- "-*-identifier-length"
|
||||
- "-*-union-access"
|
||||
- "-*-vararg"
|
||||
- "-*-macro-usage"
|
||||
- "-*-non-private-member-variables-in-classes"
|
||||
- "-*-avoid-non-const-global-variables"
|
||||
- "-*-missing-std-forward"
|
||||
- "-*-owning-memory"
|
||||
- "-*-no-malloc"
|
||||
- "*"
|
||||
- "-llvm*"
|
||||
- "-altera*"
|
||||
- "-fuchsia*"
|
||||
- "-*-use-trailing-return-type"
|
||||
- "-readability-identifier-length"
|
||||
- "-cppcoreguidelines-macro-usage"
|
||||
- "-*-avoid-c-arrays"
|
||||
- "-*-non-private-member-variables-in-classes"
|
||||
- "-*-use-anonymous-namespace"
|
||||
- "-*-reinterpret-cast"
|
||||
- "-*-noexcept-swap"
|
||||
- "-*-noexcept-move"
|
||||
- "-*-noexcept-move-constructor"
|
||||
- "-*-noexcept-move-operations"
|
||||
- "-*-bounds-array-to-pointer-decay"
|
||||
- "-*-no-array-decay"
|
||||
- "-cert-err58-cpp"
|
||||
- "-*noexcept*"
|
||||
- "-*-magic-numbers"
|
||||
- "-*-named-parameter"
|
||||
- "-misc-include-cleaner"
|
||||
- "-google-runtime-int"
|
||||
- "-bugprone-easily-swappable-parameters"
|
||||
- "-*-signed-bitwise"
|
||||
- "-readability-use-anyofallof"
|
||||
- "-readability-function-cognitive-complexity"
|
||||
- "-readability-math-missing-parentheses"
|
||||
- "-*-rvalue-reference-param-not-moved"
|
||||
- "-*-avoid-do-while"
|
||||
- "-cppcoreguidelines-pro-type-union-access"
|
||||
- "-*-copy-assignment-signature"
|
||||
- "-*-unconventional-assign-operator"
|
||||
|
7
.clangd
Normal file
7
.clangd
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
CompilerFlags:
|
||||
Add:
|
||||
- "-Wall"
|
||||
Diagnostics:
|
||||
ClangTidy:
|
||||
FastCheckFilter: None
|
11
MODULE.bazel
11
MODULE.bazel
@ -2,12 +2,15 @@
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
module(name = "asl")
|
||||
module(name = "asl", version = "0.4.0")
|
||||
|
||||
bazel_dep(name = "platforms", version = "0.0.10")
|
||||
bazel_dep(name = "rules_cc", version = "0.0.17")
|
||||
bazel_dep(name = "platforms", version = "0.0.11")
|
||||
bazel_dep(name = "rules_license", version = "1.0.0")
|
||||
|
||||
bazel_dep(name = "rules_cc", version = "0.1.1")
|
||||
cc_configure = use_extension("@rules_cc//cc:extensions.bzl", "cc_configure_extension")
|
||||
use_repo(cc_configure, "local_config_cc")
|
||||
|
||||
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
|
||||
git_override(
|
||||
module_name = "hedron_compile_commands",
|
||||
@ -15,6 +18,6 @@ git_override(
|
||||
commit = "4f28899228fb3ad0126897876f147ca15026151e",
|
||||
)
|
||||
|
||||
bazel_dep(name = "rules_python", version = "1.1.0")
|
||||
bazel_dep(name = "rules_python", version = "1.3.0")
|
||||
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
|
||||
python.toolchain(python_version = "3.13", is_default = True)
|
||||
|
88
MODULE.bazel.lock
generated
88
MODULE.bazel.lock
generated
@ -43,7 +43,8 @@
|
||||
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d",
|
||||
"https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.10/source.json": "f22828ff4cf021a6b577f1bf6341cb9dcd7965092a439f64fc1bb3b7a5ae4bd5",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
|
||||
@ -70,11 +71,12 @@
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.17/source.json": "4db99b3f55c90ab28d14552aa0632533e3e8e5e9aea0f5c24ac0014282c2a7c5",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c",
|
||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
|
||||
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
|
||||
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
|
||||
@ -88,10 +90,10 @@
|
||||
"https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.11.0/MODULE.bazel": "c3d280bc5ff1038dcb3bacb95d3f6b83da8dd27bba57820ec89ea4085da767ad",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.11.0/source.json": "302b52a39259a85aa06ca3addb9787864ca3e03b432a5f964ea68244397e7544",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.6.1/source.json": "f18d9ad3c4c54945bf422ad584fa6c5ca5b3116ff55a5b1bc77e5c1210be5960",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
|
||||
@ -121,8 +123,8 @@
|
||||
"https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.1.0/MODULE.bazel": "57e01abae22956eb96d891572490d20e07d983e0c065de0b2170cafe5053e788",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.1.0/source.json": "29f1fdfd23a40808c622f813bc93e29c3aae277333f03293f667e76159750a0f",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.3.0/source.json": "25932f917cd279c7baefa6cb1d3fa8750a7a29de522024449b19af6eab51f4a0",
|
||||
"https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
|
||||
"https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
|
||||
@ -140,44 +142,6 @@
|
||||
},
|
||||
"selectedYankedVersions": {},
|
||||
"moduleExtensions": {
|
||||
"@@platforms//host:extension.bzl%host_platform": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=",
|
||||
"usagesDigest": "SeQiIN/f8/Qt9vYQk7qcXp4I4wJeEC0RnQDiaaJ4tb8=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {
|
||||
"host_platform": {
|
||||
"repoRuleId": "@@platforms//host:extension.bzl%host_platform_repo",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"recordedRepoMappingEntries": []
|
||||
}
|
||||
},
|
||||
"@@rules_java+//java:rules_java_deps.bzl%compatibility_proxy": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "84xJEZ1jnXXwo8BXMprvBm++rRt4jsTu9liBxz0ivps=",
|
||||
"usagesDigest": "jTQDdLDxsS43zuRmg1faAjIEPWdLAbDAowI1pInQSoo=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {
|
||||
"compatibility_proxy": {
|
||||
"repoRuleId": "@@rules_java+//java:rules_java_deps.bzl%_compatibility_proxy_repo_rule",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"recordedRepoMappingEntries": [
|
||||
[
|
||||
"rules_java+",
|
||||
"bazel_tools",
|
||||
"bazel_tools"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "sFhcgPbDQehmbD1EOXzX4H1q/CD5df8zwG4kp4jbvr8=",
|
||||
@ -241,6 +205,42 @@
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"@@rules_python+//python/uv:uv.bzl%uv": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "Xpqjnjzy6zZ90Es9Wa888ZLHhn7IsNGbph/e6qoxzw8=",
|
||||
"usagesDigest": "vJ5RHUxAnV24M5swNGiAnkdxMx3Hp/iOLmNANTC5Xc8=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {
|
||||
"uv": {
|
||||
"repoRuleId": "@@rules_python+//python/uv/private:uv_toolchains_repo.bzl%uv_toolchains_repo",
|
||||
"attributes": {
|
||||
"toolchain_type": "'@@rules_python+//python/uv:uv_toolchain_type'",
|
||||
"toolchain_names": [
|
||||
"none"
|
||||
],
|
||||
"toolchain_implementations": {
|
||||
"none": "'@@rules_python+//python:none'"
|
||||
},
|
||||
"toolchain_compatible_with": {
|
||||
"none": [
|
||||
"@platforms//:incompatible"
|
||||
]
|
||||
},
|
||||
"toolchain_target_settings": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"recordedRepoMappingEntries": [
|
||||
[
|
||||
"rules_python+",
|
||||
"platforms",
|
||||
"platforms"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,14 @@ cc_library(
|
||||
hdrs = [
|
||||
"annotations.hpp",
|
||||
"assert.hpp",
|
||||
"bit.hpp",
|
||||
"config.hpp",
|
||||
"defer.hpp",
|
||||
"float.hpp",
|
||||
"functional.hpp",
|
||||
"integers.hpp",
|
||||
"meta.hpp",
|
||||
"numeric.hpp",
|
||||
"utility.hpp",
|
||||
],
|
||||
srcs = [
|
||||
@ -37,10 +39,12 @@ cc_library(
|
||||
"//asl/types:box",
|
||||
],
|
||||
) for name in [
|
||||
"bit",
|
||||
"defer",
|
||||
"float",
|
||||
"functional",
|
||||
"integers",
|
||||
"meta",
|
||||
"numeric",
|
||||
"utility",
|
||||
]]
|
||||
|
@ -4,7 +4,12 @@
|
||||
|
||||
#include "asl/base/assert.hpp"
|
||||
|
||||
#include "asl/base/meta.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(*-non-const-global-variables)
|
||||
static asl::AssertFailureHandler* s_handler = nullptr;
|
||||
|
||||
// NOLINTNEXTLINE(*-non-const-global-variables)
|
||||
static void* s_user = nullptr;
|
||||
|
||||
void asl::set_assert_failure_handler(AssertFailureHandler handler, void* user)
|
||||
|
166
asl/base/bit.hpp
Normal file
166
asl/base/bit.hpp
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "asl/base/integers.hpp"
|
||||
#include "asl/base/meta.hpp"
|
||||
|
||||
namespace asl
|
||||
{
|
||||
|
||||
constexpr bool has_single_bit(is_unsigned_integer auto x)
|
||||
{
|
||||
return x != 0 && ((x - 1) & x) == 0;
|
||||
}
|
||||
|
||||
constexpr int popcount(uint8_t v)
|
||||
{
|
||||
v = v - ((v >> 1) & 0x55);
|
||||
v = (v & 0x33) + ((v >> 2) & 0x33);
|
||||
return (v + (v >> 4)) & 0x0F;
|
||||
}
|
||||
|
||||
constexpr int popcount(uint16_t v)
|
||||
{
|
||||
v = v - ((v >> 1) & 0x5555);
|
||||
v = (v & 0x3333) + ((v >> 2) & 0x3333);
|
||||
return static_cast<uint16_t>((v + (v >> 4) & 0x0F0F) * uint16_t{0x0101}) >> 8;
|
||||
}
|
||||
|
||||
constexpr int popcount(uint32_t v)
|
||||
{
|
||||
v = v - ((v >> 1) & 0x5555'5555);
|
||||
v = (v & 0x3333'3333) + ((v >> 2) & 0x3333'3333);
|
||||
return static_cast<int>(((v + (v >> 4) & 0x0F0F'0F0F) * 0x0101'0101) >> 24);
|
||||
}
|
||||
|
||||
constexpr int popcount(uint64_t v)
|
||||
{
|
||||
v = v - ((v >> 1) & 0x5555'5555'5555'5555);
|
||||
v = (v & 0x3333'3333'3333'3333) + ((v >> 2) & 0x3333'3333'3333'3333);
|
||||
return static_cast<int>(((v + (v >> 4) & 0x0F0F'0F0F'0F0F'0F0F) * 0x0101'0101'0101'0101) >> 56);
|
||||
}
|
||||
|
||||
constexpr uint8_t propagate_right_one(uint8_t v)
|
||||
{
|
||||
v = v | (v >> 1);
|
||||
v = v | (v >> 2);
|
||||
v = v | (v >> 4);
|
||||
return v;
|
||||
}
|
||||
|
||||
constexpr uint16_t propagate_right_one(uint16_t v)
|
||||
{
|
||||
v = v | (v >> 1);
|
||||
v = v | (v >> 2);
|
||||
v = v | (v >> 4);
|
||||
v = v | (v >> 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
constexpr uint32_t propagate_right_one(uint32_t v)
|
||||
{
|
||||
v = v | (v >> 1);
|
||||
v = v | (v >> 2);
|
||||
v = v | (v >> 4);
|
||||
v = v | (v >> 8);
|
||||
v = v | (v >> 16);
|
||||
return v;
|
||||
}
|
||||
|
||||
constexpr uint64_t propagate_right_one(uint64_t v)
|
||||
{
|
||||
v = v | (v >> 1);
|
||||
v = v | (v >> 2);
|
||||
v = v | (v >> 4);
|
||||
v = v | (v >> 8);
|
||||
v = v | (v >> 16);
|
||||
v = v | (v >> 32);
|
||||
return v;
|
||||
}
|
||||
|
||||
constexpr int countr_zero(is_unsigned_integer auto v)
|
||||
{
|
||||
v = ~v & (v - 1);
|
||||
return popcount(v);
|
||||
}
|
||||
|
||||
constexpr int countr_one(is_unsigned_integer auto v)
|
||||
{
|
||||
v = v & (~v - 1);
|
||||
return popcount(v);
|
||||
}
|
||||
|
||||
constexpr int countl_zero(is_unsigned_integer auto v)
|
||||
{
|
||||
v = ~propagate_right_one(v);
|
||||
return popcount(v);
|
||||
}
|
||||
|
||||
constexpr int countl_one(is_unsigned_integer auto v)
|
||||
{
|
||||
v = ~v;
|
||||
v = ~propagate_right_one(v);
|
||||
return popcount(v);
|
||||
}
|
||||
|
||||
template<is_unsigned_integer T>
|
||||
constexpr T rotr(T v, int s);
|
||||
|
||||
template<is_unsigned_integer T>
|
||||
constexpr T rotl(T v, int s) // NOLINT(*-no-recursion)
|
||||
{
|
||||
static constexpr int N = sizeof(decltype(v)) * 8;
|
||||
s = s % N;
|
||||
return (s >= 0) ? (v << s) | (v >> (N - s)) : rotr(v, -s);
|
||||
}
|
||||
|
||||
template<is_unsigned_integer T>
|
||||
constexpr T rotr(T v, int s) // NOLINT(*-no-recursion)
|
||||
{
|
||||
static constexpr int N = sizeof(decltype(v)) * 8;
|
||||
s = s % N;
|
||||
return (s >= 0) ? (v >> s) | (v << (N - s)) : rotl(v, -s);
|
||||
}
|
||||
|
||||
constexpr uint16_t byteswap(uint16_t v)
|
||||
{
|
||||
return static_cast<uint16_t>((v << 8U) | (v >> 8U));
|
||||
}
|
||||
|
||||
constexpr uint32_t byteswap(uint32_t v)
|
||||
{
|
||||
return rotr(v & 0x00ff'00ff, 8) | rotl(v & 0xff00'ff00, 8);
|
||||
}
|
||||
|
||||
constexpr uint64_t byteswap(uint64_t v)
|
||||
{
|
||||
return (uint64_t{byteswap(static_cast<uint32_t>(v))} << 32)
|
||||
| uint64_t{byteswap(static_cast<uint32_t>(v >> 32))};
|
||||
}
|
||||
|
||||
constexpr auto bit_ceil(is_unsigned_integer auto v)
|
||||
{
|
||||
v -= 1;
|
||||
v = propagate_right_one(v);
|
||||
v += 1;
|
||||
return v;
|
||||
}
|
||||
|
||||
constexpr auto bit_floor(is_unsigned_integer auto v)
|
||||
{
|
||||
v = propagate_right_one(v);
|
||||
v = v - (v >> 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
constexpr int bit_width(is_unsigned_integer auto v)
|
||||
{
|
||||
static constexpr int N = sizeof(decltype(v)) * 8;
|
||||
return N - countl_zero(v);
|
||||
}
|
||||
|
||||
} // namespace asl
|
||||
|
194
asl/base/bit_tests.cpp
Normal file
194
asl/base/bit_tests.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "asl/base/bit.hpp"
|
||||
|
||||
#include "asl/testing/testing.hpp"
|
||||
|
||||
ASL_TEST(has_single_bit)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::has_single_bit(4U));
|
||||
ASL_TEST_EXPECT(asl::has_single_bit(1024U));
|
||||
ASL_TEST_EXPECT(asl::has_single_bit(0x8000'0000U));
|
||||
ASL_TEST_EXPECT(asl::has_single_bit(0x0000'8000'0000'0000ULL));
|
||||
ASL_TEST_EXPECT(!asl::has_single_bit(0U));
|
||||
ASL_TEST_EXPECT(!asl::has_single_bit(3U));
|
||||
ASL_TEST_EXPECT(!asl::has_single_bit(341U));
|
||||
}
|
||||
|
||||
ASL_TEST(popcount) // NOLINT(*-cognitive-complexity)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::popcount(uint8_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint8_t{255}) == 8);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint8_t{46}) == 4);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint16_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint16_t{255}) == 8);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint16_t{65535}) == 16);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint16_t{46}) == 4);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint32_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint32_t{255}) == 8);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint32_t{65535}) == 16);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint32_t{65536}) == 1);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint32_t{46}) == 4);
|
||||
ASL_TEST_EXPECT(asl::popcount(uint32_t{0xffff'ffff}) == 32);
|
||||
ASL_TEST_EXPECT(asl::popcount(0x8421'1248'8421'1248) == 16);
|
||||
ASL_TEST_EXPECT(asl::popcount(0xffff'ffff'ffff'ffff) == 64);
|
||||
}
|
||||
|
||||
ASL_TEST(countr_zero)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0}) == 8);
|
||||
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{255}) == 0);
|
||||
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0b00011100}) == 2);
|
||||
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0b10101010}) == 1);
|
||||
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0b10101001}) == 0);
|
||||
}
|
||||
|
||||
ASL_TEST(countr_one)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::countr_one(uint8_t{255}) == 8);
|
||||
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0b00011100}) == 0);
|
||||
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0b10101011}) == 2);
|
||||
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0b10101001}) == 1);
|
||||
}
|
||||
|
||||
ASL_TEST(countl_zero)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0}) == 8);
|
||||
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{255}) == 0);
|
||||
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0b00011100}) == 3);
|
||||
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0b10101010}) == 0);
|
||||
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0b00001001}) == 4);
|
||||
}
|
||||
|
||||
ASL_TEST(countl_one)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::countl_one(uint8_t{255}) == 8);
|
||||
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0b00011100}) == 0);
|
||||
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0b10101010}) == 1);
|
||||
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0b11101001}) == 3);
|
||||
}
|
||||
|
||||
ASL_TEST(rotl)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, 1) == 0x8000'0200);
|
||||
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, 3) == 0x0000'0802);
|
||||
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, 0) == 0x4000'0100);
|
||||
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, -1) == 0x2000'0080);
|
||||
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, -12) == 0x1004'0000);
|
||||
}
|
||||
|
||||
ASL_TEST(rotr)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, -1) == 0x8000'0200);
|
||||
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, -3) == 0x0000'0802);
|
||||
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, 0) == 0x4000'0100);
|
||||
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, 1) == 0x2000'0080);
|
||||
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, 12) == 0x1004'0000);
|
||||
}
|
||||
|
||||
ASL_TEST(byteswap)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::byteswap(uint32_t{0x1234'5678}) == 0x7856'3412);
|
||||
ASL_TEST_EXPECT(asl::byteswap(uint16_t{0x1234}) == 0x3412);
|
||||
ASL_TEST_EXPECT(asl::byteswap(uint64_t{0x0123'4567'89ab'cdef}) == 0xefcd'ab89'6745'2301);
|
||||
}
|
||||
|
||||
ASL_TEST(bit_ceil) // NOLINT(*-cognitive-complexity)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{3}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{4}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{5}) == 8);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{6}) == 8);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{127}) == 128);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{128}) == 128);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{254}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{255}) == 0);
|
||||
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{3}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{4}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{5}) == 8);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{6}) == 8);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{32000}) == 32768);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{32768}) == 32768);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{35000}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{65535}) == 0);
|
||||
}
|
||||
|
||||
ASL_TEST(bit_floor) // NOLINT(*-cognitive-complexity)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{3}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{4}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{5}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{6}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{127}) == 64);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{128}) == 128);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{254}) == 128);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{255}) == 128);
|
||||
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{3}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{4}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{5}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{6}) == 4);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{32000}) == 16384);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{32768}) == 32768);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{35000}) == 32768);
|
||||
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{65535}) == 32768);
|
||||
}
|
||||
|
||||
ASL_TEST(bit_width) // NOLINT(*-cognitive-complexity)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint8_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint8_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint8_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint8_t{3}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint8_t{4}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint8_t{5}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint8_t{6}) == 3);
|
||||
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{3}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{4}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{5}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{6}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint16_t{65535}) == 16);
|
||||
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{3}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{4}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{5}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{6}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{65535}) == 16);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{65536}) == 17);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint32_t{0xffff'ffff}) == 32);
|
||||
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{0}) == 0);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{1}) == 1);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{2}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{3}) == 2);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{4}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{5}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{6}) == 3);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{65535}) == 16);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{65536}) == 17);
|
||||
ASL_TEST_EXPECT(asl::bit_width(uint64_t{0xffff'ffff}) == 32);
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// NOLINTBEGIN(*-macro-to-enum)
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32)
|
||||
@ -19,3 +21,6 @@
|
||||
#else
|
||||
#error Unknown compiler
|
||||
#endif
|
||||
|
||||
// NOLINTEND(*-macro-to-enum)
|
||||
|
||||
|
@ -18,14 +18,16 @@ class DeferCallback
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
explicit DeferCallback(T&& callback) : m_callback(ASL_FWD(callback))
|
||||
explicit DeferCallback(T&& callback)
|
||||
requires (!same_as<un_cvref_t<T>, DeferCallback>)
|
||||
: m_callback(std::forward<T>(callback))
|
||||
{
|
||||
}
|
||||
|
||||
ASL_DELETE_COPY(DeferCallback);
|
||||
|
||||
DeferCallback(DeferCallback&& other) :
|
||||
m_callback(ASL_MOVE(other.m_callback)), m_moved(exchange(other.m_moved, true))
|
||||
m_callback(std::move(other.m_callback)), m_moved(exchange(other.m_moved, true))
|
||||
{
|
||||
}
|
||||
|
||||
@ -42,7 +44,7 @@ struct DeferFactory
|
||||
template<invocable Callback>
|
||||
DeferCallback<Callback> operator<<(Callback&& callback) const
|
||||
{
|
||||
return DeferCallback<Callback>(ASL_FWD(callback));
|
||||
return DeferCallback<Callback>(std::forward<Callback>(callback));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,25 +10,25 @@ ASL_TEST(defer)
|
||||
uint32_t a = 0;
|
||||
|
||||
{
|
||||
ASL_DEFER [&a]() { a |= 1; };
|
||||
ASL_DEFER [&a]() { a |= 1U; };
|
||||
ASL_TEST_EXPECT(a == 0);
|
||||
|
||||
{
|
||||
ASL_DEFER [&a]() { a |= 2; };
|
||||
ASL_DEFER [&a]() { a |= 4; };
|
||||
ASL_DEFER [&a]() { a |= 2U; };
|
||||
ASL_DEFER [&a]() { a |= 4U; };
|
||||
ASL_TEST_EXPECT(a == 0);
|
||||
}
|
||||
|
||||
ASL_TEST_EXPECT(a == 6);
|
||||
|
||||
{
|
||||
ASL_DEFER [&a]() { a |= 8; };
|
||||
ASL_DEFER [&a]() { a |= 8U; };
|
||||
ASL_TEST_EXPECT(a == 6);
|
||||
}
|
||||
|
||||
ASL_TEST_EXPECT(a == 14);
|
||||
|
||||
ASL_DEFER [&a]() { a |= 16; };
|
||||
ASL_DEFER [&a]() { a |= 16U; };
|
||||
ASL_TEST_EXPECT(a == 14);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include "asl/base/meta.hpp"
|
||||
|
||||
using float32_t = float;
|
||||
using float64_t = double;
|
||||
|
||||
namespace asl
|
||||
{
|
||||
|
||||
|
@ -9,56 +9,62 @@
|
||||
|
||||
namespace asl {
|
||||
|
||||
template<typename... Args, typename C>
|
||||
template<typename F, typename... Args>
|
||||
constexpr auto invoke(F&& f, Args&&... args)
|
||||
-> decltype(std::forward<F>(f)(std::forward<Args>(args)...))
|
||||
requires (!is_member_ptr<un_cvref_t<F>>) && requires {
|
||||
f(std::forward<Args>(args)...);
|
||||
}
|
||||
{
|
||||
return std::forward<F>(f)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
constexpr auto&& invoke(auto C::* f, same_or_derived_from<C> auto&& arg)
|
||||
{
|
||||
return std::forward<decltype(arg)>(arg).*f;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
constexpr auto&& invoke(auto C::* f, auto&& arg)
|
||||
requires (
|
||||
!same_or_derived_from<decltype(arg), C>
|
||||
&& requires { (*arg).*f; }
|
||||
)
|
||||
{
|
||||
return (*std::forward<decltype(arg)>(arg)).*f;
|
||||
}
|
||||
|
||||
template<typename C, typename... Args>
|
||||
constexpr auto invoke(is_func auto C::* f, same_or_derived_from<C> auto&& self, Args&&... args)
|
||||
-> decltype((std::forward<decltype(self)>(self).*f)(std::forward<Args>(args)...))
|
||||
requires requires { (self.*f)(std::forward<Args>(args)...); }
|
||||
{
|
||||
return (std::forward<decltype(self)>(self).*f)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename C, typename... Args>
|
||||
constexpr auto invoke(is_func auto C::* f, auto&& self, Args&&... args)
|
||||
-> decltype((self.*f)(ASL_FWD(args)...))
|
||||
requires requires {
|
||||
(self.*f)(ASL_FWD(args)...);
|
||||
}
|
||||
{
|
||||
return (ASL_FWD(self).*f)(ASL_FWD(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args, typename C>
|
||||
constexpr auto invoke(is_func auto C::* f, auto* self, Args&&... args)
|
||||
-> decltype((self->*f)(ASL_FWD(args)...))
|
||||
requires requires {
|
||||
(self->*f)(ASL_FWD(args)...);
|
||||
}
|
||||
{
|
||||
return (self->*f)(ASL_FWD(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args, typename C>
|
||||
constexpr auto invoke(is_object auto C::* m, auto&& self, Args&&...)
|
||||
-> decltype(self.*m)
|
||||
-> decltype(((*std::forward<decltype(self)>(self)).*f)(std::forward<Args>(args)...))
|
||||
requires (
|
||||
sizeof...(Args) == 0 &&
|
||||
requires { self.*m; }
|
||||
!same_or_derived_from<decltype(self), C>
|
||||
&& requires { ((*self).*f)(std::forward<Args>(args)...); }
|
||||
)
|
||||
{
|
||||
return ASL_FWD(self).*m;
|
||||
return ((*std::forward<decltype(self)>(self)).*f)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args, typename C>
|
||||
constexpr auto invoke(is_object auto C::* m, auto* self, Args&&...)
|
||||
-> decltype(self->*m)
|
||||
requires (
|
||||
sizeof...(Args) == 0 &&
|
||||
requires { self->*m; }
|
||||
)
|
||||
template<typename R, typename F, typename... Args>
|
||||
constexpr R invoke_r(F&& f, Args&&... args)
|
||||
{
|
||||
return self->*m;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
constexpr auto invoke(auto&& f, Args&&... args)
|
||||
-> decltype(f(ASL_FWD(args)...))
|
||||
requires requires {
|
||||
f(ASL_FWD(args)...);
|
||||
if constexpr (is_void<R>)
|
||||
{
|
||||
static_cast<void>(invoke(std::forward<F>(f), std::forward<Args>(args)...));
|
||||
}
|
||||
else
|
||||
{
|
||||
return invoke(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
}
|
||||
{
|
||||
return ASL_FWD(f)(ASL_FWD(args)...);
|
||||
}
|
||||
|
||||
template<typename Void, typename F, typename... Args>
|
||||
@ -76,7 +82,11 @@ using invoke_result_t = _invoke_result_helper<void, F, Args...>::type;
|
||||
template<typename F, typename... Args>
|
||||
concept invocable = requires (F&& f, Args&&... args)
|
||||
{
|
||||
invoke(ASL_FWD(f), ASL_FWD(args)...);
|
||||
invoke(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
template<typename R, typename F, typename... Args>
|
||||
concept invocable_r = invocable<F, Args...>
|
||||
&& (is_void<R> || convertible_to<invoke_result_t<F, Args...>, R>);
|
||||
|
||||
} // namespace asl
|
||||
|
@ -7,9 +7,12 @@
|
||||
|
||||
struct HasFunction
|
||||
{
|
||||
void do_something(int, float) {}
|
||||
void do_something(int, float) const {}
|
||||
int& do_something2(int, float) &;
|
||||
};
|
||||
|
||||
struct HasFunction2 : public HasFunction {};
|
||||
|
||||
struct HasMember
|
||||
{
|
||||
int member{};
|
||||
@ -17,6 +20,8 @@ struct HasMember
|
||||
void (*member_fn)(){};
|
||||
};
|
||||
|
||||
struct HasMember2 : public HasMember {};
|
||||
|
||||
struct Functor
|
||||
{
|
||||
int64_t operator()() { return 35; }
|
||||
@ -34,7 +39,13 @@ static_assert(asl::same_as<asl::invoke_result_t<Functor>, int64_t>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<Functor, int>, int>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(static_cast<float(*)(float)>(some_func1)), float>, float>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something), HasFunction, int, float>, void>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), const HasMember>, const int&>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something), const HasFunction2&, int, float>, void>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something), HasFunction*, int, float>, void>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something2), HasFunction2&, int, float>, int&>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something2), HasFunction*, int, float>, int&>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), HasMember>, int&&>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), const HasMember&>, const int&>);
|
||||
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), const HasMember2*>, const int&>);
|
||||
|
||||
static_assert(asl::invocable<int()>);
|
||||
static_assert(!asl::invocable<int(), int>);
|
||||
@ -45,8 +56,20 @@ static_assert(asl::invocable<Functor, int>);
|
||||
static_assert(!asl::invocable<Functor, void*>);
|
||||
static_assert(asl::invocable<decltype(static_cast<float(*)(float)>(some_func1)), float>);
|
||||
static_assert(asl::invocable<decltype(&HasFunction::do_something), HasFunction, int, float>);
|
||||
static_assert(asl::invocable<decltype(&HasFunction::do_something), const HasFunction2&, int, float>);
|
||||
static_assert(asl::invocable<decltype(&HasFunction::do_something), HasFunction*, int, float>);
|
||||
static_assert(!asl::invocable<decltype(&HasFunction::do_something), HasFunction, int, int*>);
|
||||
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember>);
|
||||
static_assert(!asl::invocable<decltype(&HasFunction::do_something2), HasFunction, int, float>);
|
||||
static_assert(!asl::invocable<decltype(&HasFunction::do_something2), const HasFunction2&, int, float>);
|
||||
static_assert(asl::invocable<decltype(&HasFunction::do_something2), HasFunction2&, int, float>);
|
||||
static_assert(asl::invocable<decltype(&HasFunction::do_something2), HasFunction*, int, float>);
|
||||
static_assert(!asl::invocable<decltype(&HasFunction::do_something2), HasFunction, int, int*>);
|
||||
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember2>);
|
||||
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember&>);
|
||||
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember2*>);
|
||||
|
||||
static_assert(asl::invocable_r<void*, int*()>);
|
||||
static_assert(!asl::invocable_r<int*, void*()>);
|
||||
|
||||
ASL_TEST(invoke_member_function)
|
||||
{
|
||||
@ -89,3 +112,9 @@ ASL_TEST(invoke_lambda)
|
||||
ASL_TEST_EXPECT(asl::invoke([](){ return 35; }) == 35);
|
||||
ASL_TEST_EXPECT(asl::invoke([](int x){ return x + 2; }, 6) == 8);
|
||||
}
|
||||
|
||||
ASL_TEST(invoke_r)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::invoke_r<int>([]() { return 1ULL; }) == 1);
|
||||
asl::invoke_r<void>([]() { return 1ULL; });
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ struct empty {};
|
||||
|
||||
template<typename T> struct id { using type = T; };
|
||||
|
||||
struct in_place_t {};
|
||||
static constexpr in_place_t in_place{};
|
||||
|
||||
template<typename... Args> static constexpr isize_t types_count = sizeof...(Args);
|
||||
|
||||
template<typename T, T kValue> struct integral_constant { static constexpr T value = kValue; };
|
||||
@ -54,6 +57,11 @@ template<typename T> using as_rref_t = decltype(_as_rref_helper<T>(0))::type;
|
||||
|
||||
template<typename T> consteval as_rref_t<T> declval() {}
|
||||
|
||||
template<typename T> auto _as_ptr_helper(int) -> id<T*>;
|
||||
template<typename T> auto _as_ptr_helper(...) -> id<T>;
|
||||
|
||||
template<typename T> using as_ptr_t = decltype(_as_ptr_helper<T>(0))::type;
|
||||
|
||||
template<typename T> struct _un_ref_t { using type = T; };
|
||||
template<typename T> struct _un_ref_t<T&> { using type = T; };
|
||||
template<typename T> struct _un_ref_t<T&&> { using type = T; };
|
||||
@ -87,12 +95,6 @@ template<typename T> concept trivially_destructible = __is_trivially_destructibl
|
||||
template<typename T> concept copyable = copy_constructible<T> && copy_assignable<T>;
|
||||
template<typename T> concept moveable = move_constructible<T> && move_assignable<T>;
|
||||
|
||||
template<typename To, typename From>
|
||||
concept convertible_from = __is_convertible(From, To);
|
||||
|
||||
template<typename Derived, class Base>
|
||||
concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_from<const volatile Base*, const volatile Derived*>;
|
||||
|
||||
using nullptr_t = decltype(nullptr);
|
||||
|
||||
template<typename T> struct _un_const_helper { using type = T; };
|
||||
@ -149,11 +151,20 @@ template<typename T> struct _is_ptr_helper<T*> : true_type {};
|
||||
|
||||
template<typename T> concept is_ptr = _is_ptr_helper<un_cv_t<T>>::value;
|
||||
|
||||
template<typename From, typename To>
|
||||
concept convertible_to = __is_convertible(From, To);
|
||||
|
||||
template<typename Derived, typename Base>
|
||||
concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_to<const volatile Derived*, const volatile Base*>;
|
||||
|
||||
template<typename Derived, typename Base>
|
||||
concept same_or_derived_from = same_as<un_cvref_t<Derived>, Base> || derived_from<un_cvref_t<Derived>, Base>;
|
||||
|
||||
template<typename T> struct _tame_helper { using type = T; };
|
||||
|
||||
#define TAME_HELPER_IMPL(TRAILING) \
|
||||
template<typename R, typename... Args> \
|
||||
struct _tame_helper<R(Args...) TRAILING> { using type = R(Args...); }
|
||||
struct _tame_helper<R(Args...) TRAILING> { using type = R(Args...); } // NOLINT(*-parentheses)
|
||||
|
||||
TAME_HELPER_IMPL();
|
||||
TAME_HELPER_IMPL(&);
|
||||
@ -189,13 +200,36 @@ template<typename R, typename... Args> struct _is_func_helper<R(Args...)> : true
|
||||
|
||||
template<typename T> concept is_func = _is_func_helper<tame_t<T>>::value;
|
||||
|
||||
template<typename T> struct _is_member_ptr_helper : false_type {};
|
||||
template<typename C, typename T> struct _is_member_ptr_helper<T C::*> : true_type
|
||||
{
|
||||
static constexpr bool kIsFunc = is_func<T>;
|
||||
};
|
||||
|
||||
template<typename T> concept is_member_ptr = _is_member_ptr_helper<un_cv_t<T>>::value;
|
||||
|
||||
template<typename T> concept is_member_func_ptr = is_member_ptr<T> && _is_member_ptr_helper<un_cv_t<T>>::kIsFunc;
|
||||
template<typename T> concept is_member_data_ptr = is_member_ptr<T> && !_is_member_ptr_helper<un_cv_t<T>>::kIsFunc;
|
||||
|
||||
template<typename T> concept is_object = !is_void<T> && !is_ref<T> && !is_func<T>;
|
||||
|
||||
template<typename T> struct _is_array_helper : false_type {};
|
||||
template<typename T> struct _is_array_helper<T[]> : true_type {};
|
||||
template<typename T, int N> struct _is_array_helper<T[N]> : true_type {};
|
||||
template<typename T> struct _array_helper : false_type { using type = T; };
|
||||
template<typename T> struct _array_helper<T[]> : true_type { using type = T; };
|
||||
template<typename T, int N> struct _array_helper<T[N]> : true_type { using type = T; };
|
||||
|
||||
template<typename T> concept is_array = _is_array_helper<T>::value;
|
||||
template<typename T> concept is_array = _array_helper<T>::value;
|
||||
|
||||
template<typename T> using remove_extent_t = _array_helper<T>::type;
|
||||
|
||||
template<typename T>
|
||||
using decay_t =
|
||||
select_t<
|
||||
is_array<un_ref_t<T>>,
|
||||
as_ptr_t<remove_extent_t<un_ref_t<T>>>,
|
||||
select_t<
|
||||
is_func<un_ref_t<T>>,
|
||||
as_ptr_t<un_ref_t<T>>,
|
||||
un_cv_t<un_ref_t<T>>>>;
|
||||
|
||||
template<typename T> struct _is_floating_point_helper : false_type {};
|
||||
template<> struct _is_floating_point_helper<float> : true_type {};
|
||||
@ -203,20 +237,80 @@ template<> struct _is_floating_point_helper<double> : true_type {};
|
||||
|
||||
template<typename T> concept is_floating_point = _is_floating_point_helper<un_cv_t<T>>::value;
|
||||
|
||||
template<typename T> struct _is_integer_helper : false_type {};
|
||||
template<> struct _is_integer_helper<int8_t> : true_type {};
|
||||
template<> struct _is_integer_helper<int16_t> : true_type {};
|
||||
template<> struct _is_integer_helper<int32_t> : true_type {};
|
||||
template<> struct _is_integer_helper<int64_t> : true_type {};
|
||||
template<> struct _is_integer_helper<uint8_t> : true_type {};
|
||||
template<> struct _is_integer_helper<uint16_t> : true_type {};
|
||||
template<> struct _is_integer_helper<uint32_t> : true_type {};
|
||||
template<> struct _is_integer_helper<uint64_t> : true_type {};
|
||||
template<typename T> struct _integer_traits
|
||||
{
|
||||
static constexpr bool kSigned = false;
|
||||
static constexpr bool kUnsigned = false;
|
||||
};
|
||||
|
||||
template<typename T> concept is_integer = _is_integer_helper<un_cv_t<T>>::value;
|
||||
template<> struct _integer_traits<uint8_t>
|
||||
{
|
||||
static constexpr bool kSigned = false;
|
||||
static constexpr bool kUnsigned = true;
|
||||
using as_signed = int8_t;
|
||||
};
|
||||
|
||||
template<> struct _integer_traits<uint16_t>
|
||||
{
|
||||
static constexpr bool kSigned = false;
|
||||
static constexpr bool kUnsigned = true;
|
||||
using as_signed = int16_t;
|
||||
};
|
||||
|
||||
template<> struct _integer_traits<uint32_t>
|
||||
{
|
||||
static constexpr bool kSigned = false;
|
||||
static constexpr bool kUnsigned = true;
|
||||
using as_signed = int32_t;
|
||||
};
|
||||
|
||||
template<> struct _integer_traits<uint64_t>
|
||||
{
|
||||
static constexpr bool kSigned = false;
|
||||
static constexpr bool kUnsigned = true;
|
||||
using as_signed = int64_t;
|
||||
};
|
||||
|
||||
template<> struct _integer_traits<int8_t>
|
||||
{
|
||||
static constexpr bool kSigned = true;
|
||||
static constexpr bool kUnsigned = false;
|
||||
using as_unsigned = uint8_t;
|
||||
};
|
||||
|
||||
template<> struct _integer_traits<int16_t>
|
||||
{
|
||||
static constexpr bool kSigned = true;
|
||||
static constexpr bool kUnsigned = false;
|
||||
using as_unsigned = uint16_t;
|
||||
};
|
||||
|
||||
template<> struct _integer_traits<int32_t>
|
||||
{
|
||||
static constexpr bool kSigned = true;
|
||||
static constexpr bool kUnsigned = false;
|
||||
using as_unsigned = uint32_t;
|
||||
};
|
||||
|
||||
template<> struct _integer_traits<int64_t>
|
||||
{
|
||||
static constexpr bool kSigned = true;
|
||||
static constexpr bool kUnsigned = false;
|
||||
using as_unsigned = uint64_t;
|
||||
};
|
||||
|
||||
template<typename T> concept is_signed_integer = _integer_traits<T>::kSigned;
|
||||
template<typename T> concept is_unsigned_integer = _integer_traits<T>::kUnsigned;
|
||||
|
||||
template<typename T> concept is_integer = is_signed_integer<T> || is_unsigned_integer<T>;
|
||||
|
||||
template<is_signed_integer T> using as_unsigned_integer = _integer_traits<T>::as_unsigned;
|
||||
template<is_unsigned_integer T> using as_signed_integer = _integer_traits<T>::as_signed;
|
||||
|
||||
template<typename T> concept is_enum = __is_enum(T);
|
||||
|
||||
template<is_enum T> using underlying_t = __underlying_type(T);
|
||||
|
||||
template<typename T> struct is_uniquely_represented : false_type {};
|
||||
template<is_integer T> struct is_uniquely_represented<T> : true_type {};
|
||||
template<is_enum T> struct is_uniquely_represented<T> : true_type {};
|
||||
@ -244,29 +338,28 @@ concept has_niche = constructible_from<T, niche_t> && equality_comparable_with<T
|
||||
template<typename T>
|
||||
concept is_niche = same_as<un_cvref_t<T>, niche_t>;
|
||||
|
||||
template<typename T, typename U>
|
||||
concept _derefs_with_indirection_as = requires(T& t)
|
||||
template<typename From, typename To>
|
||||
concept _dereferenceable_as_convertible = requires(From& t)
|
||||
{
|
||||
*t;
|
||||
requires convertible_from<U&, decltype(*t)>;
|
||||
{ *t } -> convertible_to<To&>;
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
concept _derefs_reference_as = is_ref<T> && convertible_from<U&, T>;
|
||||
template<typename From, typename To>
|
||||
concept derefs_as = is_object<To> &&
|
||||
(convertible_to<un_ref_t<From>&, To&> || _dereferenceable_as_convertible<un_ref_t<From>, To>);
|
||||
|
||||
template<typename T, typename U>
|
||||
concept _derefs_value_as = !is_ref<T> && convertible_from<U&, T&>;
|
||||
|
||||
template<typename U, _derefs_with_indirection_as<U> T>
|
||||
constexpr U& deref(T&& t) { return static_cast<U&>(*t); }
|
||||
|
||||
template<typename U, _derefs_reference_as<U> T>
|
||||
constexpr U& deref(T&& t) { return static_cast<U&>(t); }
|
||||
|
||||
template<typename U, _derefs_value_as<U> T>
|
||||
constexpr U& deref(T&& t) { return static_cast<U&>(t); }
|
||||
|
||||
template<typename T, typename U>
|
||||
concept derefs_as = _derefs_with_indirection_as<T, U> || _derefs_reference_as<T, U> || _derefs_value_as<T, U>;
|
||||
template<typename To, derefs_as<To> From>
|
||||
constexpr auto&& deref(From&& from) // NOLINT(*-missing-std-forward)
|
||||
{
|
||||
if constexpr (_dereferenceable_as_convertible<From, To>)
|
||||
{
|
||||
using deref_type = decltype(*declval<From&&>());
|
||||
return static_cast<copy_cref_t<deref_type, To>>(*static_cast<From&&>(from));
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<copy_cref_t<From&&, To>>(from);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace asl
|
||||
|
@ -135,6 +135,12 @@ static_assert(!asl::is_ref<void>);
|
||||
static_assert(!asl::is_ref<void()>);
|
||||
static_assert(!asl::is_ref<void() const &&>);
|
||||
|
||||
struct MyClass
|
||||
{
|
||||
int data;
|
||||
int fn(int x) { return x; } // NOLINT
|
||||
};
|
||||
|
||||
static_assert(asl::is_ptr<int*>);
|
||||
static_assert(asl::is_ptr<const int* const>);
|
||||
static_assert(asl::is_ptr<const volatile int*>);
|
||||
@ -142,6 +148,28 @@ static_assert(!asl::is_ptr<int>);
|
||||
static_assert(!asl::is_ptr<void>);
|
||||
static_assert(!asl::is_ptr<void()>);
|
||||
static_assert(!asl::is_ptr<void() const &&>);
|
||||
static_assert(!asl::is_ptr<int MyClass::*>);
|
||||
static_assert(!asl::is_ptr<int (MyClass::*)(int)>);
|
||||
|
||||
static_assert(!asl::is_member_ptr<int*>);
|
||||
static_assert(!asl::is_member_ptr<void()>);
|
||||
static_assert(!asl::is_member_ptr<void() const &&>);
|
||||
static_assert(asl::is_member_ptr<int MyClass::*>);
|
||||
static_assert(asl::is_member_ptr<int (MyClass::*)(int)>);
|
||||
|
||||
static_assert(!asl::is_member_data_ptr<int*>);
|
||||
static_assert(!asl::is_member_data_ptr<void()>);
|
||||
static_assert(!asl::is_member_data_ptr<void() const &&>);
|
||||
static_assert(asl::is_member_data_ptr<int MyClass::*>);
|
||||
static_assert(!asl::is_member_data_ptr<int (MyClass::*)(int)>);
|
||||
|
||||
static_assert(!asl::is_member_func_ptr<int*>);
|
||||
static_assert(!asl::is_member_func_ptr<void()>);
|
||||
static_assert(!asl::is_member_func_ptr<void() const &&>);
|
||||
static_assert(!asl::is_member_func_ptr<int MyClass::*>);
|
||||
static_assert(asl::is_member_func_ptr<int (MyClass::*)(int)>);
|
||||
static_assert(asl::is_member_func_ptr<int (MyClass::*)(int) const>);
|
||||
static_assert(asl::is_member_func_ptr<int (MyClass::*)(int) volatile &&>);
|
||||
|
||||
static_assert(asl::same_as<int, asl::tame_t<int>>);
|
||||
static_assert(asl::same_as<int(), asl::tame_t<int()>>);
|
||||
@ -189,6 +217,10 @@ static_assert(!asl::is_array<void>);
|
||||
static_assert(!asl::is_array<void(int)>);
|
||||
static_assert(!asl::is_array<int(float) const && noexcept>);
|
||||
|
||||
static_assert(asl::same_as<int, asl::remove_extent_t<int>>);
|
||||
static_assert(asl::same_as<int, asl::remove_extent_t<int[]>>);
|
||||
static_assert(asl::same_as<int, asl::remove_extent_t<int[67]>>);
|
||||
|
||||
static_assert(asl::same_as<int, asl::un_ref_t<int>>);
|
||||
static_assert(asl::same_as<int, asl::un_ref_t<int&>>);
|
||||
static_assert(asl::same_as<int, asl::un_ref_t<int&&>>);
|
||||
@ -205,21 +237,23 @@ class C {};
|
||||
class D { public: operator C() { return c; } C c; }; // NOLINT
|
||||
class E { public: template<class T> E(T&&) {} }; // NOLINT
|
||||
|
||||
static_assert(asl::convertible_from<Base*, Derived*>);
|
||||
static_assert(!asl::convertible_from<Derived*, Base*>);
|
||||
static_assert(asl::convertible_from<C, D>);
|
||||
static_assert(!asl::convertible_from<C*, Derived*>);
|
||||
static_assert(asl::convertible_from<E, Base>);
|
||||
static_assert(asl::convertible_to<Derived*, Base*>);
|
||||
static_assert(!asl::convertible_to<Base*, Derived*>);
|
||||
static_assert(asl::convertible_to<D, C>);
|
||||
static_assert(!asl::convertible_to<Derived*, C*>);
|
||||
static_assert(asl::convertible_to<Base, E>);
|
||||
|
||||
static_assert(!asl::convertible_from<int16_t(&)[], int32_t(&)[]>);
|
||||
static_assert(asl::convertible_from<const int16_t(&)[], int16_t(&)[]>);
|
||||
static_assert(asl::convertible_from<const int16_t(&)[], const int16_t(&)[]>);
|
||||
static_assert(asl::convertible_from<int16_t(&)[], int16_t(&)[]>);
|
||||
static_assert(!asl::convertible_from<int32_t(&)[], int16_t(&)[]>);
|
||||
static_assert(!asl::convertible_from<int16_t(&)[], const int16_t(&)[]>);
|
||||
static_assert(!asl::convertible_from<C(&)[], D(&)[]>);
|
||||
static_assert(!asl::convertible_to<int32_t(&)[], int16_t(&)[]>);
|
||||
static_assert(asl::convertible_to<int16_t(&)[], const int16_t(&)[]>);
|
||||
static_assert(asl::convertible_to<const int16_t(&)[], const int16_t(&)[]>);
|
||||
static_assert(asl::convertible_to<int16_t(&)[], int16_t(&)[]>);
|
||||
static_assert(!asl::convertible_to<int16_t(&)[], int32_t(&)[]>);
|
||||
static_assert(!asl::convertible_to<const int16_t(&)[], int16_t(&)[]>);
|
||||
static_assert(!asl::convertible_to<D(&)[], C(&)[]>);
|
||||
|
||||
static_assert(asl::derived_from<Derived, Base>);
|
||||
static_assert(asl::derived_from<Derived, Derived>);
|
||||
static_assert(asl::derived_from<Base, Base>);
|
||||
static_assert(!asl::derived_from<Base, Derived>);
|
||||
static_assert(!asl::derived_from<D, C>);
|
||||
static_assert(!asl::derived_from<C, D>);
|
||||
@ -227,6 +261,16 @@ static_assert(!asl::derived_from<uint8_t, uint16_t>);
|
||||
static_assert(!asl::derived_from<uint16_t, uint8_t>);
|
||||
static_assert(!asl::derived_from<int, int>);
|
||||
|
||||
static_assert(asl::same_or_derived_from<Derived, Base>);
|
||||
static_assert(asl::same_or_derived_from<Derived, Derived>);
|
||||
static_assert(asl::same_or_derived_from<Base, Base>);
|
||||
static_assert(!asl::same_or_derived_from<Base, Derived>);
|
||||
static_assert(!asl::same_or_derived_from<D, C>);
|
||||
static_assert(!asl::same_or_derived_from<C, D>);
|
||||
static_assert(!asl::same_or_derived_from<uint8_t, uint16_t>);
|
||||
static_assert(!asl::same_or_derived_from<uint16_t, uint8_t>);
|
||||
static_assert(asl::same_or_derived_from<int, int>);
|
||||
|
||||
static_assert(!asl::is_const<int>);
|
||||
static_assert(asl::is_const<const int>);
|
||||
static_assert(!asl::is_const<const int*>);
|
||||
@ -255,16 +299,22 @@ static_assert(asl::is_enum<Enum2>);
|
||||
|
||||
static_assert(asl::derefs_as<int, int>);
|
||||
static_assert(asl::derefs_as<int*, int>);
|
||||
static_assert(!asl::derefs_as<const int*, int>);
|
||||
static_assert(asl::derefs_as<int&, int>);
|
||||
static_assert(!asl::derefs_as<const int&, int>);
|
||||
static_assert(asl::derefs_as<asl::box<int>, int>);
|
||||
|
||||
static_assert(asl::derefs_as<Derived, Base>);
|
||||
static_assert(!asl::derefs_as<Base, Derived>);
|
||||
static_assert(asl::derefs_as<Derived*, Base>);
|
||||
static_assert(asl::derefs_as<Derived&, Base>);
|
||||
static_assert(!asl::derefs_as<Base&, Derived>);
|
||||
static_assert(asl::derefs_as<asl::box<Derived>, Base>);
|
||||
static_assert(asl::derefs_as<asl::box<Derived>, Derived>);
|
||||
static_assert(asl::derefs_as<asl::box<Base>, Base>);
|
||||
|
||||
static void wants_int(int) {}
|
||||
static void wants_base(Base&) {}
|
||||
static void wants_base(const Base&) {}
|
||||
static void wants_base_ptr(Base*) {}
|
||||
|
||||
ASL_TEST(deref)
|
||||
@ -285,10 +335,11 @@ ASL_TEST(deref)
|
||||
wants_base(asl::deref<Base>(&c));
|
||||
wants_base(asl::deref<Base>(d));
|
||||
|
||||
wants_base_ptr(&asl::deref<Base>(Derived{}));
|
||||
wants_base_ptr(&asl::deref<Base>(c));
|
||||
wants_base_ptr(&asl::deref<Base>(&c));
|
||||
wants_base_ptr(&asl::deref<Base>(d));
|
||||
|
||||
wants_base(asl::deref<Base>(std::move(d)));
|
||||
}
|
||||
|
||||
static_assert(asl::same_as<asl::copy_cref_t<int, float>, float>);
|
||||
@ -311,3 +362,20 @@ static_assert(asl::same_as<asl::copy_cref_t<const int&, const float>, const floa
|
||||
static_assert(asl::same_as<asl::copy_cref_t<const int&, float&&>, const float&>);
|
||||
static_assert(asl::same_as<asl::copy_cref_t<const int&, const float&>, const float&>);
|
||||
|
||||
static_assert(asl::same_as<asl::decay_t<int>, int>);
|
||||
static_assert(!asl::same_as<asl::decay_t<int>, float>);
|
||||
static_assert(asl::same_as<asl::decay_t<int&>, int>);
|
||||
static_assert(asl::same_as<asl::decay_t<int&&>, int>);
|
||||
static_assert(asl::same_as<asl::decay_t<const int&>, int>);
|
||||
static_assert(asl::same_as<asl::decay_t<int[2]>, int*>);
|
||||
static_assert(!asl::same_as<asl::decay_t<int[4][2]>, int*>);
|
||||
static_assert(!asl::same_as<asl::decay_t<int[4][2]>, int**>);
|
||||
static_assert(asl::same_as<asl::decay_t<int[4][2]>, int(*)[2]>);
|
||||
static_assert(asl::same_as<asl::decay_t<int(int)>, int(*)(int)>);
|
||||
|
||||
enum EnumU8 : uint8_t {};
|
||||
enum EnumI64 : int64_t {};
|
||||
|
||||
static_assert(asl::same_as<asl::underlying_t<EnumU8>, uint8_t>);
|
||||
static_assert(asl::same_as<asl::underlying_t<EnumI64>, int64_t>);
|
||||
|
||||
|
43
asl/base/numeric.hpp
Normal file
43
asl/base/numeric.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "asl/base/integers.hpp"
|
||||
#include "asl/base/bit.hpp"
|
||||
#include "asl/base/meta.hpp"
|
||||
|
||||
namespace asl
|
||||
{
|
||||
|
||||
template<is_integer T>
|
||||
constexpr bool is_pow2(T x)
|
||||
{
|
||||
using unsigned_type = select_t<is_unsigned_integer<T>, T, as_unsigned_integer<T>>;
|
||||
return x > 0 && has_single_bit(static_cast<unsigned_type>(x));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept is_numeric = is_integer<T> || is_floating_point<T>;
|
||||
|
||||
template<is_numeric T>
|
||||
constexpr T min(T a, T b)
|
||||
{
|
||||
return (a <= b) ? a : b;
|
||||
}
|
||||
|
||||
template<is_numeric T>
|
||||
constexpr T max(T a, T b)
|
||||
{
|
||||
return (a >= b) ? a : b;
|
||||
}
|
||||
|
||||
template<is_numeric T>
|
||||
constexpr T clamp(T x, T a, T b)
|
||||
{
|
||||
return min(max(x, a), b);
|
||||
}
|
||||
|
||||
} // namespace asl
|
||||
|
16
asl/base/numeric_tests.cpp
Normal file
16
asl/base/numeric_tests.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "asl/base/numeric.hpp"
|
||||
|
||||
#include "asl/testing/testing.hpp"
|
||||
|
||||
ASL_TEST(is_pow2)
|
||||
{
|
||||
ASL_TEST_EXPECT(asl::is_pow2(4));
|
||||
ASL_TEST_EXPECT(asl::is_pow2(65536));
|
||||
ASL_TEST_EXPECT(!asl::is_pow2(6));
|
||||
ASL_TEST_EXPECT(!asl::is_pow2(1978));
|
||||
ASL_TEST_EXPECT(!asl::is_pow2(0));
|
||||
}
|
@ -7,31 +7,59 @@
|
||||
#include "asl/base/meta.hpp"
|
||||
#include "asl/base/assert.hpp"
|
||||
|
||||
#define ASL_MOVE(...) (static_cast<::asl::un_ref_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__))
|
||||
namespace std
|
||||
{
|
||||
|
||||
#define ASL_FWD(expr_) (static_cast<decltype(expr_)&&>(expr_))
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr asl::un_ref_t<T>&& move(T&& t) noexcept // NOLINT
|
||||
{
|
||||
return static_cast<asl::un_ref_t<T>&&>(t);
|
||||
}
|
||||
|
||||
#define ASL_FWD_LIKE(ref_, expr_) (static_cast<::asl::copy_cref_t<ref_, decltype(expr_)&&>>(expr_))
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr T&& forward(asl::un_ref_t<T>& t) noexcept // NOLINT
|
||||
{
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
template< class T >
|
||||
[[nodiscard]] constexpr T&& forward(asl::un_ref_t<T>&& t) noexcept // NOLINT
|
||||
{
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
[[nodiscard]] constexpr auto forward_like(U&& x) noexcept -> asl::copy_cref_t<T, U> // NOLINT
|
||||
{
|
||||
using return_type = asl::copy_cref_t<T, U&&>;
|
||||
return static_cast<return_type>(x);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr T* launder(T* ptr) noexcept // NOLINT
|
||||
requires (!asl::is_func<T> && !asl::is_void<T>)
|
||||
{
|
||||
return __builtin_launder(ptr);
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace asl
|
||||
{
|
||||
|
||||
struct in_place_t {};
|
||||
static constexpr in_place_t in_place{};
|
||||
|
||||
template<moveable T>
|
||||
constexpr void swap(T& a, T& b)
|
||||
{
|
||||
T tmp{ASL_MOVE(a)};
|
||||
a = ASL_MOVE(b);
|
||||
b = ASL_MOVE(tmp);
|
||||
T tmp{std::move(a)};
|
||||
a = std::move(b);
|
||||
b = std::move(tmp);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T exchange(T& obj, U&& new_value)
|
||||
{
|
||||
T old_value = ASL_MOVE(obj);
|
||||
obj = ASL_FWD(new_value);
|
||||
T old_value = std::move(obj);
|
||||
obj = std::forward<U>(new_value);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
@ -41,62 +69,37 @@ constexpr U bit_cast(T value) requires (sizeof(T) == sizeof(U))
|
||||
return __builtin_bit_cast(U, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T min(T a, T b)
|
||||
template<is_enum T>
|
||||
constexpr auto to_underlying(T value)
|
||||
{
|
||||
return (a <= b) ? a : b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T max(T a, T b)
|
||||
{
|
||||
return (a >= b) ? a : b;
|
||||
}
|
||||
|
||||
constexpr uint64_t round_up_pow2(uint64_t v)
|
||||
{
|
||||
ASL_ASSERT(v <= 0x8000'0000'0000'0000);
|
||||
|
||||
v -= 1;
|
||||
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v |= v >> 32;
|
||||
|
||||
return v + 1;
|
||||
}
|
||||
|
||||
constexpr bool is_pow2(isize_t v)
|
||||
{
|
||||
return v > 0 && ((v - 1) & v) == 0;
|
||||
return static_cast<underlying_t<T>>(value);
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(*-macro-parentheses)
|
||||
#define ASL_DELETE_COPY(T) \
|
||||
T(const T&) = delete; \
|
||||
T& operator=(const T&) = delete;
|
||||
T& operator=(const T&) = delete
|
||||
|
||||
#define ASL_DELETE_MOVE(T) \
|
||||
T(T&&) = delete; \
|
||||
T& operator=(T&&) = delete;
|
||||
T& operator=(T&&) = delete
|
||||
|
||||
#define ASL_DELETE_COPY_MOVE(T) \
|
||||
ASL_DELETE_COPY(T) \
|
||||
ASL_DELETE_COPY(T); \
|
||||
ASL_DELETE_MOVE(T)
|
||||
|
||||
#define ASL_DEFAULT_COPY(T) \
|
||||
T(const T&) = default; \
|
||||
T& operator=(const T&) = default;
|
||||
T& operator=(const T&) = default
|
||||
|
||||
#define ASL_DEFAULT_MOVE(T) \
|
||||
T(T&&) = default; \
|
||||
T& operator=(T&&) = default;
|
||||
T& operator=(T&&) = default
|
||||
|
||||
#define ASL_DEFAULT_COPY_MOVE(T) \
|
||||
ASL_DEFAULT_COPY(T) \
|
||||
ASL_DEFAULT_COPY(T); \
|
||||
ASL_DEFAULT_MOVE(T)
|
||||
// NOLINTEND(*-macro-parentheses)
|
||||
|
||||
#define ASL_CONCAT2(A, B) A##B
|
||||
#define ASL_CONCAT(A, B) ASL_CONCAT2(A, B)
|
||||
|
@ -12,28 +12,28 @@ template<typename T> static constexpr int identify(T&&) { return 4; }
|
||||
|
||||
struct IdentifySelf
|
||||
{
|
||||
constexpr int get(this auto&& self) { return identify(ASL_FWD(self)); }
|
||||
constexpr int get(this auto&& self) { return identify(std::forward<decltype(self)>(self)); }
|
||||
};
|
||||
|
||||
static int get_const_lref(const IdentifySelf& i) { return ASL_FWD(i).get(); }
|
||||
static int get_const_rref(const IdentifySelf&& i) { return ASL_FWD(i).get(); }
|
||||
static int get_lref(IdentifySelf& i) { return ASL_FWD(i).get(); }
|
||||
static int get_rref(IdentifySelf&& i) { return ASL_FWD(i).get(); }
|
||||
static int get_const_lref(const IdentifySelf& i) { return i.get(); }
|
||||
static int get_const_rref(const IdentifySelf&& i) { return std::move(i).get(); } // NOLINT
|
||||
static int get_lref(IdentifySelf& i) { return i.get(); }
|
||||
static int get_rref(IdentifySelf&& i) { return std::move(i).get(); } // NOLINT
|
||||
|
||||
ASL_TEST(forward)
|
||||
{
|
||||
IdentifySelf id;
|
||||
ASL_TEST_EXPECT(get_const_lref(id) == 1);
|
||||
IdentifySelf id{};
|
||||
ASL_TEST_EXPECT(get_const_lref(IdentifySelf{}) == 1);
|
||||
ASL_TEST_EXPECT(get_lref(id) == 3);
|
||||
ASL_TEST_EXPECT(get_const_rref(ASL_MOVE(id)) == 2);
|
||||
ASL_TEST_EXPECT(get_rref(ASL_MOVE(id)) == 4);
|
||||
ASL_TEST_EXPECT(get_const_rref(IdentifySelf{}) == 2);
|
||||
ASL_TEST_EXPECT(get_rref(IdentifySelf{}) == 4);
|
||||
}
|
||||
|
||||
ASL_TEST(move)
|
||||
{
|
||||
IdentifySelf id;
|
||||
ASL_TEST_EXPECT(id.get() == 3);
|
||||
ASL_TEST_EXPECT(ASL_MOVE(id).get() == 4);
|
||||
ASL_TEST_EXPECT(IdentifySelf{}.get() == 4);
|
||||
}
|
||||
|
||||
struct Level1
|
||||
@ -51,33 +51,50 @@ struct Level3
|
||||
Level2 deeper;
|
||||
};
|
||||
|
||||
static int get_const_lref(const Level3& i) { return ASL_FWD(i).deeper.deeper.id.get(); }
|
||||
static int get_const_rref(const Level3&& i) { return ASL_FWD(i).deeper.deeper.id.get(); }
|
||||
static int get_lref(Level3& i) { return ASL_FWD(i).deeper.deeper.id.get(); }
|
||||
static int get_rref(Level3&& i) { return ASL_FWD(i).deeper.deeper.id.get(); }
|
||||
static int get_const_lref(const Level3& i) { return i.deeper.deeper.id.get(); }
|
||||
static int get_const_rref(const Level3&& i) { return std::move(i).deeper.deeper.id.get(); }
|
||||
static int get_lref(Level3& i) { return i.deeper.deeper.id.get(); }
|
||||
static int get_rref(Level3&& i) { return std::move(i).deeper.deeper.id.get(); }
|
||||
|
||||
ASL_TEST(forward2)
|
||||
{
|
||||
Level3 id{};
|
||||
ASL_TEST_EXPECT(get_const_lref(id) == 1);
|
||||
ASL_TEST_EXPECT(get_lref(id) == 3);
|
||||
ASL_TEST_EXPECT(get_const_rref(ASL_MOVE(id)) == 2);
|
||||
ASL_TEST_EXPECT(get_rref(ASL_MOVE(id)) == 4);
|
||||
ASL_TEST_EXPECT(get_const_rref(Level3{}) == 2);
|
||||
ASL_TEST_EXPECT(get_rref(Level3{}) == 4);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static int test_fwd_like(T&& t)
|
||||
static int test_fwd_like(T) // NOLINT
|
||||
{
|
||||
IdentifySelf id;
|
||||
return ASL_FWD_LIKE(decltype(t), id).get();
|
||||
const IdentifySelf id;
|
||||
return std::forward_like<T>(id).get();
|
||||
}
|
||||
|
||||
ASL_TEST(forward_like)
|
||||
{
|
||||
int x{};
|
||||
ASL_TEST_EXPECT(test_fwd_like<const int&>(x) == 1);
|
||||
ASL_TEST_EXPECT(test_fwd_like<const int&>(7) == 1);
|
||||
ASL_TEST_EXPECT(test_fwd_like<int&>(x) == 3);
|
||||
ASL_TEST_EXPECT(test_fwd_like<const int&&>(ASL_MOVE(x)) == 2);
|
||||
ASL_TEST_EXPECT(test_fwd_like<int&&>(ASL_MOVE(x)) == 4);
|
||||
ASL_TEST_EXPECT(test_fwd_like<const int&&>(8) == 2);
|
||||
ASL_TEST_EXPECT(test_fwd_like<int&&>(9) == 4);
|
||||
}
|
||||
|
||||
enum class Enum : int // NOLINT
|
||||
{
|
||||
kOne = 1,
|
||||
kTwo = 2,
|
||||
};
|
||||
|
||||
enum Enum2 : int // NOLINT
|
||||
{
|
||||
kOne = 1,
|
||||
kTwo = 2,
|
||||
};
|
||||
|
||||
static_assert(asl::to_underlying(Enum::kOne) == 1);
|
||||
static_assert(asl::to_underlying(Enum::kTwo) == 2);
|
||||
static_assert(asl::to_underlying(kOne) == 1);
|
||||
static_assert(asl::to_underlying(kTwo) == 2);
|
||||
|
||||
|
@ -13,6 +13,7 @@ cc_library(
|
||||
],
|
||||
deps = [
|
||||
"//asl/memory",
|
||||
"//asl/memory:allocator",
|
||||
"//asl/base",
|
||||
"//asl/types:span",
|
||||
"//asl/hashing",
|
||||
@ -28,6 +29,7 @@ cc_library(
|
||||
deps = [
|
||||
"//asl/base",
|
||||
"//asl/memory",
|
||||
"//asl/memory:allocator",
|
||||
"//asl/types:maybe_uninit",
|
||||
"//asl/hashing",
|
||||
],
|
||||
@ -42,6 +44,7 @@ cc_library(
|
||||
deps = [
|
||||
"//asl/base",
|
||||
"//asl/memory",
|
||||
"//asl/memory:allocator",
|
||||
"//asl/hashing",
|
||||
":hash_set",
|
||||
],
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "asl/memory/memory.hpp"
|
||||
#include "asl/base/annotations.hpp"
|
||||
#include "asl/base/assert.hpp"
|
||||
#include "asl/base/bit.hpp"
|
||||
#include "asl/types/span.hpp"
|
||||
#include "asl/hashing/hash.hpp"
|
||||
|
||||
@ -46,7 +47,7 @@ private:
|
||||
static_assert(align_of<T*> == align_of<isize_t>);
|
||||
static_assert(align_of<T*> == align_of<size_t>);
|
||||
|
||||
constexpr size_t load_size_encoded() const
|
||||
[[nodiscard]] constexpr size_t load_size_encoded() const
|
||||
{
|
||||
size_t s{};
|
||||
asl::memcpy(&s, &m_size_encoded_, sizeof(size_t));
|
||||
@ -80,25 +81,25 @@ private:
|
||||
{
|
||||
return is_on_heap(size_encoded)
|
||||
? static_cast<isize_t>(size_encoded & (~kOnHeapMask))
|
||||
: static_cast<isize_t>(size_encoded >> 56);
|
||||
: static_cast<isize_t>(size_encoded >> 56U);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool is_on_heap() const
|
||||
[[nodiscard]] constexpr bool is_on_heap() const
|
||||
{
|
||||
return is_on_heap(load_size_encoded());
|
||||
}
|
||||
|
||||
constexpr T* push_uninit()
|
||||
{
|
||||
isize_t sz = size();
|
||||
const isize_t sz = size();
|
||||
resize_uninit_inner(sz + 1);
|
||||
return data() + sz;
|
||||
}
|
||||
|
||||
constexpr void resize_uninit_inner(isize_t new_size)
|
||||
{
|
||||
isize_t old_size = size();
|
||||
const isize_t old_size = size();
|
||||
if (!trivially_destructible<T> && new_size < old_size)
|
||||
{
|
||||
destroy_n(data() + new_size, old_size - new_size);
|
||||
@ -110,7 +111,9 @@ private:
|
||||
constexpr void set_size_inline(isize_t new_size)
|
||||
{
|
||||
ASL_ASSERT(new_size >= 0 && new_size <= kInlineCapacity);
|
||||
size_t size_encoded = (load_size_encoded() & size_t{0x00ff'ffff'ffff'ffff}) | (bit_cast<size_t>(new_size) << 56);
|
||||
const size_t size_encoded =
|
||||
(load_size_encoded() & size_t{0x00ff'ffff'ffff'ffff})
|
||||
| (bit_cast<size_t>(new_size) << 56U);
|
||||
store_size_encoded(size_encoded);
|
||||
}
|
||||
|
||||
@ -144,8 +147,8 @@ private:
|
||||
}
|
||||
else if (!assign || m_allocator == other.m_allocator)
|
||||
{
|
||||
isize_t other_n = other.size();
|
||||
isize_t this_n = size();
|
||||
const isize_t other_n = other.size();
|
||||
const isize_t this_n = size();
|
||||
resize_uninit_inner(other_n);
|
||||
if (other_n <= this_n)
|
||||
{
|
||||
@ -160,7 +163,7 @@ private:
|
||||
else
|
||||
{
|
||||
destroy();
|
||||
isize_t n = other.size();
|
||||
const isize_t n = other.size();
|
||||
ASL_ASSERT(n <= kInlineCapacity);
|
||||
relocate_uninit_n(data(), other.data(), n);
|
||||
set_size_inline(n);
|
||||
@ -170,14 +173,14 @@ private:
|
||||
|
||||
if (assign)
|
||||
{
|
||||
m_allocator = ASL_MOVE(other.m_allocator);
|
||||
m_allocator = std::move(other.m_allocator);
|
||||
}
|
||||
}
|
||||
|
||||
void copy_range(span<const T> to_copy)
|
||||
{
|
||||
isize_t this_size = size();
|
||||
isize_t new_size = to_copy.size();
|
||||
const isize_t this_size = size();
|
||||
const isize_t new_size = to_copy.size();
|
||||
|
||||
resize_uninit_inner(to_copy.size());
|
||||
ASL_ASSERT(capacity() >= new_size);
|
||||
@ -209,7 +212,7 @@ private:
|
||||
// NOLINTNEXTLINE(*-pointer-arithmetic)
|
||||
for (T* it = data_ptr + old_size; it < end; ++it)
|
||||
{
|
||||
construct_at<T>(it, ASL_FWD(args)...);
|
||||
construct_at<T>(it, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,11 +227,11 @@ public:
|
||||
}
|
||||
|
||||
explicit constexpr buffer(Allocator allocator)
|
||||
: m_allocator{ASL_MOVE(allocator)}
|
||||
: m_allocator{std::move(allocator)}
|
||||
{}
|
||||
|
||||
explicit constexpr buffer(span<const T> s, Allocator allocator)
|
||||
: m_allocator{ASL_MOVE(allocator)}
|
||||
: m_allocator{std::move(allocator)}
|
||||
{
|
||||
copy_range(s);
|
||||
}
|
||||
@ -242,9 +245,9 @@ public:
|
||||
|
||||
constexpr buffer(buffer&& other)
|
||||
requires moveable<T>
|
||||
: buffer(ASL_MOVE(other.m_allocator))
|
||||
: buffer(std::move(other.m_allocator))
|
||||
{
|
||||
move_from_other(ASL_MOVE(other), false);
|
||||
move_from_other(std::move(other), false);
|
||||
}
|
||||
|
||||
constexpr buffer& operator=(const buffer& other)
|
||||
@ -259,7 +262,7 @@ public:
|
||||
requires moveable<T>
|
||||
{
|
||||
if (&other == this) { return *this; }
|
||||
move_from_other(ASL_MOVE(other), true);
|
||||
move_from_other(std::move(other), true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -268,12 +271,14 @@ public:
|
||||
destroy();
|
||||
}
|
||||
|
||||
constexpr isize_t size() const
|
||||
[[nodiscard]] constexpr isize_t size() const
|
||||
{
|
||||
return decode_size(load_size_encoded());
|
||||
}
|
||||
|
||||
constexpr isize_t capacity() const
|
||||
[[nodiscard]] constexpr bool is_empty() const { return size() == 0; }
|
||||
|
||||
[[nodiscard]] constexpr isize_t capacity() const
|
||||
{
|
||||
if constexpr (kInlineCapacity == 0)
|
||||
{
|
||||
@ -287,7 +292,7 @@ public:
|
||||
|
||||
void clear()
|
||||
{
|
||||
isize_t current_size = size();
|
||||
const isize_t current_size = size();
|
||||
if (current_size == 0) { return; }
|
||||
|
||||
destroy_n(data(), current_size);
|
||||
@ -316,7 +321,7 @@ public:
|
||||
if (new_capacity <= capacity()) { return; }
|
||||
ASL_ASSERT(new_capacity > kInlineCapacity);
|
||||
|
||||
new_capacity = static_cast<isize_t>(round_up_pow2(static_cast<uint64_t>(new_capacity)));
|
||||
new_capacity = static_cast<isize_t>(bit_ceil(static_cast<uint64_t>(new_capacity)));
|
||||
|
||||
T* old_data = data();
|
||||
const isize_t old_capacity = capacity();
|
||||
@ -328,12 +333,12 @@ public:
|
||||
|
||||
if (currently_on_heap && trivially_move_constructible<T>)
|
||||
{
|
||||
m_data = reinterpret_cast<T*>(m_allocator.realloc(m_data, old_layout, new_layout));
|
||||
m_data = static_cast<T*>(m_allocator.realloc(m_data, old_layout, new_layout));
|
||||
m_capacity = new_capacity;
|
||||
return;
|
||||
}
|
||||
|
||||
T* new_data = reinterpret_cast<T*>(m_allocator.alloc(new_layout));
|
||||
T* new_data = static_cast<T*>(m_allocator.alloc(new_layout));
|
||||
|
||||
relocate_uninit_n(new_data, old_data, current_size);
|
||||
|
||||
@ -357,7 +362,7 @@ public:
|
||||
constexpr void resize_zero(isize_t new_size)
|
||||
requires trivially_default_constructible<T> && trivially_destructible<T>
|
||||
{
|
||||
isize_t old_size = size();
|
||||
const isize_t old_size = size();
|
||||
resize_uninit(new_size);
|
||||
|
||||
if (new_size > old_size)
|
||||
@ -381,20 +386,25 @@ public:
|
||||
requires constructible_from<T, decltype(args)&&...>
|
||||
{
|
||||
T* uninit = push_uninit();
|
||||
T* init = construct_at<T>(uninit, ASL_FWD(args)...);
|
||||
T* init = construct_at<T>(uninit, std::forward<decltype(args)>(args)...);
|
||||
return *init;
|
||||
}
|
||||
|
||||
auto data(this auto&& self)
|
||||
{
|
||||
using return_type = un_ref_t<copy_cref_t<decltype(self), T>>*;
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
auto&& buffer = reinterpret_cast<copy_cref_t<decltype(self), class buffer>>(self);
|
||||
if constexpr (kInlineCapacity == 0)
|
||||
{
|
||||
return return_type{ self.m_data };
|
||||
return return_type{ buffer.m_data };
|
||||
}
|
||||
else
|
||||
{
|
||||
return self.is_on_heap() ? return_type{ self.m_data } : reinterpret_cast<return_type>(&self);
|
||||
return buffer.is_on_heap()
|
||||
? return_type{ buffer.m_data }
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
: std::launder(reinterpret_cast<return_type>(&buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,12 +420,12 @@ public:
|
||||
return contiguous_iterator<type>{self.data() + self.size()};
|
||||
}
|
||||
|
||||
constexpr operator span<const T>() const // NOLINT(*-explicit-conversions)
|
||||
constexpr operator span<const T>() const // NOLINT(*explicit*)
|
||||
{
|
||||
return as_span();
|
||||
}
|
||||
|
||||
constexpr operator span<T>() // NOLINT(*-explicit-conversions)
|
||||
constexpr operator span<T>() // NOLINT(*explicit*)
|
||||
{
|
||||
return as_span();
|
||||
}
|
||||
@ -429,14 +439,14 @@ public:
|
||||
constexpr auto&& operator[](this auto&& self, isize_t i)
|
||||
{
|
||||
ASL_ASSERT(i >= 0 && i <= self.size());
|
||||
return ASL_FWD_LIKE(decltype(self), ASL_FWD(self).data()[i]);
|
||||
return std::forward_like<decltype(self)>(std::forward<decltype(self)>(self).data()[i]);
|
||||
}
|
||||
|
||||
template<typename H>
|
||||
requires hashable<T>
|
||||
friend H AslHashValue(H h, const buffer& b)
|
||||
{
|
||||
return H::combine_contiguous(ASL_MOVE(h), b.as_span());
|
||||
return H::combine_contiguous(std::move(h), b.as_span());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ ASL_TEST(default_size)
|
||||
ASL_TEST_EXPECT(b1.capacity() == 5);
|
||||
ASL_TEST_EXPECT(static_cast<const void*>(b1.data()) == &b1);
|
||||
|
||||
asl::buffer<Big> b2;
|
||||
const asl::buffer<Big> b2;
|
||||
ASL_TEST_EXPECT(b2.size() == 0);
|
||||
ASL_TEST_EXPECT(b2.capacity() == 0);
|
||||
ASL_TEST_EXPECT(b2.data() == nullptr);
|
||||
@ -34,6 +34,7 @@ struct CounterAllocator
|
||||
{
|
||||
isize_t* count;
|
||||
|
||||
[[nodiscard]]
|
||||
void* alloc(const asl::layout& layout) const
|
||||
{
|
||||
*count += 1;
|
||||
@ -76,6 +77,7 @@ struct IncompatibleAllocator
|
||||
};
|
||||
static_assert(asl::allocator<IncompatibleAllocator>);
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(reserve_capacity)
|
||||
{
|
||||
isize_t count = 0;
|
||||
@ -105,6 +107,7 @@ ASL_TEST(reserve_capacity)
|
||||
ASL_TEST_EXPECT(count == 2);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(push)
|
||||
{
|
||||
asl::buffer<int32_t> b;
|
||||
@ -137,7 +140,7 @@ ASL_TEST(push)
|
||||
|
||||
ASL_TEST(from_span)
|
||||
{
|
||||
int data[] = {1, 2, 4, 8};
|
||||
const int data[] = {1, 2, 4, 8};
|
||||
asl::buffer<int> b{data};
|
||||
|
||||
ASL_TEST_EXPECT(b.size() == 4);
|
||||
@ -157,12 +160,14 @@ struct MoveableType
|
||||
MoveableType(MoveableType&& other) : moved{other.moved + 1}, value{other.value} {}
|
||||
MoveableType& operator=(const MoveableType&) = delete;
|
||||
MoveableType& operator=(MoveableType&&) = delete;
|
||||
~MoveableType() = default;
|
||||
};
|
||||
static_assert(!asl::trivially_copy_constructible<MoveableType>);
|
||||
static_assert(!asl::trivially_move_constructible<MoveableType>);
|
||||
static_assert(!asl::copyable<MoveableType>);
|
||||
static_assert(asl::move_constructible<MoveableType>);
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(push_move)
|
||||
{
|
||||
asl::buffer<MoveableType> b;
|
||||
@ -275,14 +280,13 @@ ASL_TEST(move_construct_from_heap)
|
||||
buf.push(&d[2]);
|
||||
|
||||
{
|
||||
asl::buffer<DestructorObserver> buf2(ASL_MOVE(buf));
|
||||
const asl::buffer<DestructorObserver> buf2(std::move(buf));
|
||||
ASL_TEST_EXPECT(buf2.size() == 3);
|
||||
ASL_TEST_EXPECT(d[0] == false);
|
||||
ASL_TEST_EXPECT(d[1] == false);
|
||||
ASL_TEST_EXPECT(d[2] == false);
|
||||
}
|
||||
|
||||
ASL_TEST_EXPECT(buf.size() == 0);
|
||||
ASL_TEST_EXPECT(d[0] == true);
|
||||
ASL_TEST_EXPECT(d[1] == true);
|
||||
ASL_TEST_EXPECT(d[2] == true);
|
||||
@ -294,12 +298,11 @@ ASL_TEST(move_construct_inline_trivial)
|
||||
buf.push(1U);
|
||||
buf.push(2U);
|
||||
|
||||
asl::buffer<uint64_t> buf2(ASL_MOVE(buf));
|
||||
asl::buffer<uint64_t> buf2(std::move(buf));
|
||||
ASL_TEST_EXPECT(buf2[0] == 1U);
|
||||
ASL_TEST_EXPECT(buf2[1] == 2U);
|
||||
|
||||
ASL_TEST_EXPECT(buf2.size() == 2);
|
||||
ASL_TEST_EXPECT(buf.size() == 0);
|
||||
}
|
||||
|
||||
ASL_TEST(move_construct_from_inline_non_trivial)
|
||||
@ -310,17 +313,17 @@ ASL_TEST(move_construct_from_inline_non_trivial)
|
||||
buf.push(&d[1]);
|
||||
|
||||
{
|
||||
asl::buffer<DestructorObserver> buf2(ASL_MOVE(buf));
|
||||
const asl::buffer<DestructorObserver> buf2(std::move(buf));
|
||||
ASL_TEST_EXPECT(buf2.size() == 2);
|
||||
ASL_TEST_EXPECT(d[0] == false);
|
||||
ASL_TEST_EXPECT(d[1] == false);
|
||||
}
|
||||
|
||||
ASL_TEST_EXPECT(buf.size() == 0);
|
||||
ASL_TEST_EXPECT(d[0] == true);
|
||||
ASL_TEST_EXPECT(d[1] == true);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(move_assign_from_heap)
|
||||
{
|
||||
bool d[6]{};
|
||||
@ -344,9 +347,8 @@ ASL_TEST(move_assign_from_heap)
|
||||
ASL_TEST_EXPECT(d[4] == false);
|
||||
ASL_TEST_EXPECT(d[5] == false);
|
||||
|
||||
buf2 = ASL_MOVE(buf);
|
||||
buf2 = std::move(buf);
|
||||
|
||||
ASL_TEST_EXPECT(buf.size() == 0);
|
||||
ASL_TEST_EXPECT(buf2.size() == 3);
|
||||
|
||||
ASL_TEST_EXPECT(d[0] == false);
|
||||
@ -380,11 +382,10 @@ ASL_TEST(move_assign_trivial_heap_to_inline)
|
||||
buf2.push(5);
|
||||
ASL_TEST_EXPECT(alloc_count == 1);
|
||||
|
||||
buf = ASL_MOVE(buf2);
|
||||
buf = std::move(buf2);
|
||||
ASL_TEST_EXPECT(alloc_count == 1);
|
||||
|
||||
ASL_TEST_EXPECT(buf.size() == 3);
|
||||
ASL_TEST_EXPECT(buf2.size() == 0);
|
||||
ASL_TEST_EXPECT(buf[0] == 3);
|
||||
ASL_TEST_EXPECT(buf[1] == 4);
|
||||
ASL_TEST_EXPECT(buf[2] == 5);
|
||||
@ -405,15 +406,15 @@ ASL_TEST(move_assign_trivial_inline_to_heap)
|
||||
buf2.push(5);
|
||||
ASL_TEST_EXPECT(alloc_count == 1);
|
||||
|
||||
buf2 = ASL_MOVE(buf);
|
||||
buf2 = std::move(buf);
|
||||
ASL_TEST_EXPECT(alloc_count == 1);
|
||||
|
||||
ASL_TEST_EXPECT(buf.size() == 0);
|
||||
ASL_TEST_EXPECT(buf2.size() == 2);
|
||||
ASL_TEST_EXPECT(buf2[0] == 1);
|
||||
ASL_TEST_EXPECT(buf2[1] == 2);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(move_assign_inline_to_heap)
|
||||
{
|
||||
bool d[6]{};
|
||||
@ -430,9 +431,8 @@ ASL_TEST(move_assign_inline_to_heap)
|
||||
buf2.push(&d[4]);
|
||||
buf2.push(&d[5]);
|
||||
|
||||
buf2 = ASL_MOVE(buf);
|
||||
buf2 = std::move(buf);
|
||||
|
||||
ASL_TEST_EXPECT(buf.size() == 0);
|
||||
ASL_TEST_EXPECT(buf2.size() == 2);
|
||||
ASL_TEST_EXPECT(d[0] == false);
|
||||
ASL_TEST_EXPECT(d[1] == false);
|
||||
@ -450,6 +450,7 @@ ASL_TEST(move_assign_inline_to_heap)
|
||||
ASL_TEST_EXPECT(d[5] == true);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(move_assign_from_inline_incompatible_allocator)
|
||||
{
|
||||
bool d[6]{};
|
||||
@ -466,9 +467,8 @@ ASL_TEST(move_assign_from_inline_incompatible_allocator)
|
||||
buf2.push(&d[4]);
|
||||
buf2.push(&d[5]);
|
||||
|
||||
buf2 = ASL_MOVE(buf);
|
||||
buf2 = std::move(buf);
|
||||
|
||||
ASL_TEST_EXPECT(buf.size() == 0);
|
||||
ASL_TEST_EXPECT(buf2.size() == 2);
|
||||
ASL_TEST_EXPECT(d[0] == false);
|
||||
ASL_TEST_EXPECT(d[1] == false);
|
||||
|
@ -77,25 +77,17 @@ public:
|
||||
constexpr hash_map() requires default_constructible<Allocator> = default;
|
||||
|
||||
explicit constexpr hash_map(Allocator allocator)
|
||||
: Base{ASL_MOVE(allocator)}
|
||||
: Base{std::move(allocator)}
|
||||
{}
|
||||
|
||||
hash_map(const hash_map&) requires copyable<K> && copyable<V> = default;
|
||||
|
||||
hash_map& operator=(const hash_map&) requires copyable<K> && copyable<V> = default;
|
||||
|
||||
hash_map(hash_map&&) = default;
|
||||
|
||||
hash_map& operator=(hash_map&&) = default;
|
||||
|
||||
~hash_map() = default;
|
||||
|
||||
using Base::destroy;
|
||||
|
||||
using Base::clear;
|
||||
|
||||
using Base::size;
|
||||
|
||||
using Base::is_empty;
|
||||
|
||||
using Base::remove;
|
||||
|
||||
using Base::contains;
|
||||
@ -122,7 +114,7 @@ public:
|
||||
{
|
||||
ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0);
|
||||
|
||||
Base::m_values[result.first_available_index].construct_unsafe(ASL_MOVE(Base::m_values[result.already_present_index].as_init_unsafe()));
|
||||
Base::m_values[result.first_available_index].construct_unsafe(std::move(Base::m_values[result.already_present_index].as_init_unsafe()));
|
||||
Base::m_values[result.already_present_index].destroy_unsafe();
|
||||
|
||||
Base::m_tags[result.first_available_index] = result.tag;
|
||||
@ -133,17 +125,17 @@ public:
|
||||
|
||||
if constexpr (sizeof...(Args1) == 0 && assignable_from<V&, Arg0&&>)
|
||||
{
|
||||
Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_FWD(arg0);
|
||||
Base::m_values[result.first_available_index].as_init_unsafe().value = std::forward<Arg0>(arg0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_MOVE(V{ASL_FWD(arg0), ASL_FWD(args1)...});
|
||||
Base::m_values[result.first_available_index].as_init_unsafe().value = std::move(V{std::forward<Arg0>(arg0), std::forward<Args1>(args1)...});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0);
|
||||
Base::m_values[result.first_available_index].construct_unsafe(ASL_FWD(key), V{ASL_FWD(arg0), ASL_FWD(args1)...});
|
||||
Base::m_values[result.first_available_index].construct_unsafe(std::forward<U>(key), V{std::forward<Arg0>(arg0), std::forward<Args1>(args1)...});
|
||||
Base::m_tags[result.first_available_index] = result.tag;
|
||||
Base::m_size += 1;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "asl/testing/testing.hpp"
|
||||
#include "asl/containers/hash_map.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(default)
|
||||
{
|
||||
asl::hash_map<int, int> map;
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "asl/base/annotations.hpp"
|
||||
#include "asl/base/utility.hpp"
|
||||
#include "asl/base/meta.hpp"
|
||||
#include "asl/base/bit.hpp"
|
||||
#include "asl/base/numeric.hpp"
|
||||
#include "asl/memory/allocator.hpp"
|
||||
#include "asl/memory/memory.hpp"
|
||||
#include "asl/types/maybe_uninit.hpp"
|
||||
@ -72,10 +74,10 @@ protected:
|
||||
|
||||
ASL_NO_UNIQUE_ADDRESS Allocator m_allocator;
|
||||
|
||||
constexpr isize_t max_size() const
|
||||
[[nodiscard]] constexpr isize_t max_size() const
|
||||
{
|
||||
// Max load factor is 75%
|
||||
return (m_capacity >> 1) + (m_capacity >> 2);
|
||||
return (m_capacity >> 1) + (m_capacity >> 2); // NOLINT(*-signed-bitwise)
|
||||
}
|
||||
|
||||
static isize_t size_to_capacity(isize_t size)
|
||||
@ -83,7 +85,7 @@ protected:
|
||||
ASL_ASSERT(size > 0);
|
||||
return max<isize_t>(
|
||||
kMinCapacity,
|
||||
static_cast<isize_t>(round_up_pow2((static_cast<uint64_t>(size) * 4 + 2) / 3)));
|
||||
static_cast<isize_t>(bit_ceil((static_cast<uint64_t>(size) * 4 + 2) / 3)));
|
||||
}
|
||||
|
||||
static void insert_inner(
|
||||
@ -115,7 +117,7 @@ protected:
|
||||
*size += 1;
|
||||
}
|
||||
|
||||
values[result.first_available_index].construct_unsafe(ASL_MOVE(value));
|
||||
values[result.first_available_index].construct_unsafe(std::move(value));
|
||||
tags[result.first_available_index] = result.tag;
|
||||
}
|
||||
|
||||
@ -131,8 +133,8 @@ protected:
|
||||
{
|
||||
ASL_ASSERT(new_capacity >= kMinCapacity && is_pow2(new_capacity) && new_capacity > m_capacity);
|
||||
|
||||
auto* new_tags = reinterpret_cast<uint8_t*>(m_allocator.alloc(layout::array<uint8_t>(new_capacity)));
|
||||
auto* new_values = reinterpret_cast<maybe_uninit<T>*>(m_allocator.alloc(layout::array<maybe_uninit<T>>(new_capacity)));
|
||||
auto* new_tags = static_cast<uint8_t*>(m_allocator.alloc(layout::array<uint8_t>(new_capacity)));
|
||||
auto* new_values = static_cast<maybe_uninit<T>*>(m_allocator.alloc(layout::array<maybe_uninit<T>>(new_capacity)));
|
||||
asl::memzero(new_tags, new_capacity);
|
||||
|
||||
isize_t new_size = 0;
|
||||
@ -144,7 +146,7 @@ protected:
|
||||
{
|
||||
if ((m_tags[i] & kHasValue) == 0) { continue; }
|
||||
|
||||
insert_inner(ASL_MOVE(m_values[i].as_init_unsafe()), new_tags, new_values, new_capacity, &new_size);
|
||||
insert_inner(std::move(m_values[i].as_init_unsafe()), new_tags, new_values, new_capacity, &new_size);
|
||||
|
||||
// Destroy now so that destroy() has less things to do
|
||||
m_values[i].destroy_unsafe();
|
||||
@ -228,7 +230,6 @@ protected:
|
||||
result.tag = static_cast<uint8_t>(hash & kHashMask) | kHasValue;
|
||||
|
||||
// NOLINTBEGIN(*-pointer-arithmetic)
|
||||
|
||||
for (
|
||||
isize_t i = starting_index;
|
||||
i != starting_index || result.first_available_index < 0;
|
||||
@ -312,7 +313,7 @@ public:
|
||||
{}
|
||||
|
||||
explicit constexpr hash_set(Allocator allocator)
|
||||
: m_allocator{ASL_MOVE(allocator)}
|
||||
: m_allocator{std::move(allocator)}
|
||||
{}
|
||||
|
||||
hash_set(const hash_set& other)
|
||||
@ -339,7 +340,7 @@ public:
|
||||
, m_values{exchange(other.m_values, nullptr)}
|
||||
, m_capacity{exchange(other.m_capacity, 0)}
|
||||
, m_size{exchange(other.m_size, 0)}
|
||||
, m_allocator{ASL_MOVE(other.m_allocator)}
|
||||
, m_allocator{std::move(other.m_allocator)}
|
||||
{}
|
||||
|
||||
hash_set& operator=(hash_set&& other)
|
||||
@ -351,7 +352,7 @@ public:
|
||||
m_values = exchange(other.m_values, nullptr);
|
||||
m_capacity = exchange(other.m_capacity, 0);
|
||||
m_size = exchange(other.m_size, 0);
|
||||
m_allocator = ASL_MOVE(other.m_allocator);
|
||||
m_allocator = std::move(other.m_allocator);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -385,7 +386,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
constexpr isize_t size() const { return m_size; }
|
||||
[[nodiscard]] constexpr isize_t size() const { return m_size; }
|
||||
|
||||
[[nodiscard]] constexpr bool is_empty() const { return m_size == 0; }
|
||||
|
||||
template<typename... Args>
|
||||
void insert(Args&&... args)
|
||||
@ -393,7 +396,7 @@ public:
|
||||
{
|
||||
maybe_grow_to_fit_one_more();
|
||||
ASL_ASSERT(m_size < max_size());
|
||||
insert_inner(ASL_MOVE(T{ASL_FWD(args)...}), m_tags, m_values, m_capacity, &m_size);
|
||||
insert_inner(T{std::forward<Args>(args)...}, m_tags, m_values, m_capacity, &m_size);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
ASL_TEST(empty)
|
||||
{
|
||||
asl::hash_set<int> set;
|
||||
const asl::hash_set<int> set;
|
||||
|
||||
ASL_TEST_EXPECT(set.size() == 0);
|
||||
|
||||
@ -42,7 +42,7 @@ ASL_TEST(a_bunch_of_ints)
|
||||
{
|
||||
asl::hash_set<int> set;
|
||||
|
||||
int count = 3000;
|
||||
const int count = 3000;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
@ -98,6 +98,7 @@ struct CustomHasher
|
||||
}
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(destructor_and_remove)
|
||||
{
|
||||
static constexpr int kCount = 200;
|
||||
@ -113,9 +114,9 @@ ASL_TEST(destructor_and_remove)
|
||||
|
||||
ASL_TEST_EXPECT(set.size() == kCount);
|
||||
|
||||
for (int i = 0; i < kCount; ++i)
|
||||
for (const bool i : destroyed)
|
||||
{
|
||||
ASL_TEST_EXPECT(!destroyed[i]); // NOLINT
|
||||
ASL_TEST_EXPECT(!i); // NOLINT
|
||||
}
|
||||
|
||||
for (int i = 0; i < kCount; i += 2)
|
||||
@ -132,9 +133,9 @@ ASL_TEST(destructor_and_remove)
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < kCount; ++i)
|
||||
for (const bool i : destroyed)
|
||||
{
|
||||
ASL_TEST_EXPECT(destroyed[i]); // NOLINT
|
||||
ASL_TEST_EXPECT(i); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +148,7 @@ ASL_TEST(copy)
|
||||
set1.insert(i);
|
||||
}
|
||||
|
||||
asl::hash_set<int> set2 = set1;
|
||||
const asl::hash_set<int> set2 = set1;
|
||||
asl::hash_set<int> set3;
|
||||
set3 = set1;
|
||||
|
||||
@ -170,7 +171,7 @@ ASL_TEST(move)
|
||||
set1.insert(i);
|
||||
}
|
||||
|
||||
asl::hash_set<int> set2 = ASL_MOVE(set1);
|
||||
asl::hash_set<int> set2 = std::move(set1);
|
||||
|
||||
ASL_TEST_EXPECT(set2.size() == 100);
|
||||
for (int i = 0; i < 100; ++i)
|
||||
@ -178,7 +179,7 @@ ASL_TEST(move)
|
||||
ASL_TEST_EXPECT(set2.contains(i));
|
||||
}
|
||||
|
||||
set1 = ASL_MOVE(set2);
|
||||
set1 = std::move(set2);
|
||||
|
||||
ASL_TEST_EXPECT(set1.size() == 100);
|
||||
for (int i = 0; i < 100; ++i)
|
||||
|
@ -19,7 +19,7 @@ struct intrusive_list_node
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept is_intrusive_list_node = convertible_from<intrusive_list_node<T>*, T*>;
|
||||
concept is_intrusive_list_node = derived_from<T, intrusive_list_node<T>>;
|
||||
|
||||
template<is_intrusive_list_node T>
|
||||
class IntrusiveList
|
||||
@ -43,11 +43,11 @@ public:
|
||||
push_front(head);
|
||||
}
|
||||
|
||||
ASL_DELETE_COPY(IntrusiveList)
|
||||
ASL_DEFAULT_MOVE(IntrusiveList)
|
||||
ASL_DELETE_COPY(IntrusiveList);
|
||||
ASL_DEFAULT_MOVE(IntrusiveList);
|
||||
~IntrusiveList() = default;
|
||||
|
||||
constexpr bool is_empty() const { return m_head == nullptr; }
|
||||
[[nodiscard]] constexpr bool is_empty() const { return m_head == nullptr; }
|
||||
|
||||
void push_front(T* node)
|
||||
{
|
||||
@ -151,7 +151,7 @@ public:
|
||||
, m_advanced{end}
|
||||
{}
|
||||
|
||||
constexpr bool operator==(const generic_iterator& other) const = default;
|
||||
constexpr bool operator==(this generic_iterator, generic_iterator other) = default;
|
||||
|
||||
constexpr generic_iterator& operator++()
|
||||
{
|
||||
@ -167,9 +167,9 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
constexpr TT& operator*() const { return *m_node; }
|
||||
constexpr TT& operator*(this generic_iterator self) { return *self.m_node; }
|
||||
|
||||
constexpr TT* operator->() const { return m_node; }
|
||||
constexpr TT* operator->(this generic_iterator self) { return self.m_node; }
|
||||
};
|
||||
|
||||
using iterator = generic_iterator<T>;
|
||||
@ -188,5 +188,5 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace asl
|
||||
|
||||
|
@ -23,6 +23,7 @@ ASL_TEST(empty_list)
|
||||
ASL_TEST_EXPECT(list.back() == nullptr);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(push_front)
|
||||
{
|
||||
IntNode one{1};
|
||||
@ -75,6 +76,7 @@ ASL_TEST(push_front)
|
||||
ASL_TEST_ASSERT(it == end);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-complexity)
|
||||
ASL_TEST(push_back)
|
||||
{
|
||||
IntNode one{1};
|
||||
|
@ -151,7 +151,7 @@ asl::string_view asl::format_uint64(uint64_t v, asl::span<char, kMaxUint64Digits
|
||||
|
||||
while (v >= 100)
|
||||
{
|
||||
uint64_t x = v % 100;
|
||||
const uint64_t x = v % 100;
|
||||
v /= 100;
|
||||
write_two(s_pairs.subspan(static_cast<isize_t>(x * 2)).first<2>());
|
||||
}
|
||||
@ -195,7 +195,7 @@ void asl::AslFormat(Formatter& f, int64_t v)
|
||||
if (v < 0)
|
||||
{
|
||||
f.write("-");
|
||||
uint64_t absolute_value = ~(bit_cast<uint64_t>(v) - 1);
|
||||
const uint64_t absolute_value = ~(bit_cast<uint64_t>(v) - 1);
|
||||
AslFormat(f, absolute_value);
|
||||
}
|
||||
else
|
||||
|
@ -32,7 +32,7 @@ struct type_erased_arg
|
||||
template<formattable T>
|
||||
static constexpr void erased_fn(Formatter& f, const void* data)
|
||||
{
|
||||
AslFormat(f, *reinterpret_cast<const T*>(data));
|
||||
AslFormat(f, *static_cast<const T*>(data));
|
||||
}
|
||||
|
||||
template<formattable T>
|
||||
@ -44,7 +44,7 @@ struct type_erased_arg
|
||||
|
||||
void format(Writer*, string_view fmt, span<const type_erased_arg> args);
|
||||
|
||||
} // namespace internals
|
||||
} // namespace format_internals
|
||||
|
||||
class Formatter
|
||||
{
|
||||
@ -60,7 +60,7 @@ public:
|
||||
m_writer->write(as_bytes(s.as_span()));
|
||||
}
|
||||
|
||||
constexpr Writer* writer() const { return m_writer; }
|
||||
[[nodiscard]] constexpr Writer* writer() const { return m_writer; }
|
||||
};
|
||||
|
||||
template<formattable... Args>
|
||||
@ -68,7 +68,7 @@ void format(Writer* w, string_view fmt, const Args&... args)
|
||||
{
|
||||
if constexpr (types_count<Args...> > 0)
|
||||
{
|
||||
format_internals::type_erased_arg type_erased_args[] = {
|
||||
const format_internals::type_erased_arg type_erased_args[] = {
|
||||
format_internals::type_erased_arg(args)...
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "asl/formatting/format.hpp"
|
||||
#include "asl/base/float.hpp"
|
||||
#include "asl/base/numeric.hpp"
|
||||
|
||||
#define JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED 0
|
||||
#define JKJ_STATIC_DATA_SECTION_DEFINED 0
|
||||
@ -25,12 +26,12 @@ static constexpr char kZeros[kZeroCount] = {
|
||||
|
||||
static constexpr bool is_zero(float x)
|
||||
{
|
||||
return (asl::bit_cast<uint32_t>(x) & 0x7fff'ffff) == 0;
|
||||
return (asl::bit_cast<uint32_t>(x) & 0x7fff'ffffU) == 0;
|
||||
}
|
||||
|
||||
static constexpr bool is_zero(double x)
|
||||
{
|
||||
return (asl::bit_cast<uint64_t>(x) & 0x7fff'ffff'ffff'ffff) == 0;
|
||||
return (asl::bit_cast<uint64_t>(x) & 0x7fff'ffff'ffff'ffffULL) == 0;
|
||||
}
|
||||
|
||||
template<asl::is_floating_point T>
|
||||
@ -66,15 +67,15 @@ static void format_float(asl::Formatter& f, T value)
|
||||
if (decimal.is_negative) { f.write("-"); }
|
||||
|
||||
char buffer[20];
|
||||
asl::string_view digits = asl::format_uint64(decimal.significand, buffer);
|
||||
const asl::string_view digits = asl::format_uint64(decimal.significand, buffer);
|
||||
|
||||
if (decimal.exponent >= 0)
|
||||
{
|
||||
f.write(digits);
|
||||
while (decimal.exponent > 0)
|
||||
{
|
||||
isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount);
|
||||
f.write(asl::string_view(kZeros, to_write));
|
||||
const isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount);
|
||||
f.write(asl::string_view(static_cast<const char*>(kZeros), to_write));
|
||||
decimal.exponent -= to_write;
|
||||
}
|
||||
}
|
||||
@ -86,8 +87,8 @@ static void format_float(asl::Formatter& f, T value)
|
||||
decimal.exponent = -decimal.exponent - static_cast<int>(digits.size());
|
||||
while (decimal.exponent > 0)
|
||||
{
|
||||
isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount);
|
||||
f.write(asl::string_view(kZeros, to_write));
|
||||
const isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount);
|
||||
f.write(asl::string_view(static_cast<const char*>(kZeros), to_write));
|
||||
decimal.exponent -= to_write;
|
||||
}
|
||||
f.write(digits);
|
||||
|
@ -80,7 +80,7 @@ struct HashState
|
||||
{
|
||||
auto bytes = as_bytes(s);
|
||||
auto hashed = city_hash::CityHash128WithSeed(
|
||||
reinterpret_cast<const char*>(bytes.data()),
|
||||
reinterpret_cast<const char*>(bytes.data()), // NOLINT(*-reinterpret-cast)
|
||||
static_cast<size_t>(bytes.size()),
|
||||
h.state);
|
||||
return HashState{hashed};
|
||||
@ -89,7 +89,7 @@ struct HashState
|
||||
{
|
||||
for (const auto& value: s)
|
||||
{
|
||||
h = AslHashValue(ASL_MOVE(h), value);
|
||||
h = AslHashValue(std::move(h), value);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@ -103,7 +103,7 @@ struct HashState
|
||||
template<hashable_generic<HashState> Arg, hashable_generic<HashState>... Remaining>
|
||||
static constexpr HashState combine(HashState h, const Arg& arg, const Remaining&... remaining)
|
||||
{
|
||||
return combine(AslHashValue(ASL_MOVE(h), arg), remaining...);
|
||||
return combine(AslHashValue(std::move(h), arg), remaining...);
|
||||
}
|
||||
};
|
||||
|
||||
@ -113,13 +113,13 @@ concept hashable = hashable_generic<T, HashState>;
|
||||
template<typename H, uniquely_represented T>
|
||||
constexpr H AslHashValue(H h, const T& value)
|
||||
{
|
||||
return H::combine_contiguous(ASL_MOVE(h), span<const T>{&value, 1});
|
||||
return H::combine_contiguous(std::move(h), span<const T>{&value, 1});
|
||||
}
|
||||
|
||||
template<typename H>
|
||||
constexpr H AslHashValue(H h, bool value)
|
||||
{
|
||||
return AslHashValue(ASL_MOVE(h), value ? 1 : 0);
|
||||
return AslHashValue(std::move(h), value ? 1 : 0);
|
||||
}
|
||||
|
||||
template<typename H, typename T>
|
||||
@ -128,7 +128,7 @@ constexpr void AslHashValue(H h, T*); // Don't hash pointers
|
||||
template<typename H, hashable T>
|
||||
constexpr H AslHashValue(H h, const span<T>& s)
|
||||
{
|
||||
return H::combine_contiguous(ASL_MOVE(h), span<const T>{s.data(), s.size()});
|
||||
return H::combine_contiguous(std::move(h), span<const T>{s.data(), s.size()});
|
||||
}
|
||||
|
||||
template<hashable T>
|
||||
|
@ -29,10 +29,10 @@ static_assert(asl::hashable<int64_t>);
|
||||
|
||||
ASL_TEST(integers)
|
||||
{
|
||||
uint64_t a = asl::hash_value<uint16_t>(45);
|
||||
uint64_t b = asl::hash_value<uint16_t>(45);
|
||||
uint64_t c = asl::hash_value<uint16_t>(46);
|
||||
uint64_t d = asl::hash_value<uint32_t>(45);
|
||||
const uint64_t a = asl::hash_value<uint16_t>(45);
|
||||
const uint64_t b = asl::hash_value<uint16_t>(45);
|
||||
const uint64_t c = asl::hash_value<uint16_t>(46);
|
||||
const uint64_t d = asl::hash_value<uint32_t>(45);
|
||||
|
||||
ASL_TEST_EXPECT(a == b);
|
||||
ASL_TEST_EXPECT(a != c);
|
||||
@ -187,19 +187,19 @@ static_assert(asl::uniquely_represented<asl::option<NonZero>>);
|
||||
|
||||
ASL_TEST(option)
|
||||
{
|
||||
asl::option<int> int1 = 0;
|
||||
asl::option<int> int2 = 0;
|
||||
asl::option<int> int3 = 1;
|
||||
asl::option<int> int4 = asl::nullopt;
|
||||
const asl::option<int> int1 = 0;
|
||||
const asl::option<int> int2 = 0;
|
||||
const asl::option<int> int3 = 1;
|
||||
const asl::option<int> int4 = asl::nullopt;
|
||||
|
||||
ASL_TEST_EXPECT(asl::hash_value(int1) == asl::hash_value(int2));
|
||||
ASL_TEST_EXPECT(asl::hash_value(int1) != asl::hash_value(int3));
|
||||
ASL_TEST_EXPECT(asl::hash_value(int1) != asl::hash_value(int4));
|
||||
|
||||
asl::option<NonZero> noz1{8};
|
||||
asl::option<NonZero> noz2{8};
|
||||
asl::option<NonZero> noz3{9};
|
||||
asl::option<NonZero> noz4 = asl::nullopt;
|
||||
const asl::option<NonZero> noz1{8};
|
||||
const asl::option<NonZero> noz2{8};
|
||||
const asl::option<NonZero> noz3{9};
|
||||
const asl::option<NonZero> noz4 = asl::nullopt;
|
||||
|
||||
ASL_TEST_EXPECT(asl::hash_value(noz1) == asl::hash_value(noz2));
|
||||
ASL_TEST_EXPECT(asl::hash_value(noz1) != asl::hash_value(noz3));
|
||||
@ -208,17 +208,18 @@ ASL_TEST(option)
|
||||
|
||||
static_assert(asl::hashable<asl::status>);
|
||||
|
||||
// NOLINTNEXTLINE(*-cognitive-complexity)
|
||||
ASL_TEST(status)
|
||||
{
|
||||
asl::status s1 = asl::ok();
|
||||
asl::status s2 = asl::ok();
|
||||
asl::status s3 = asl::internal_error();
|
||||
asl::status s4 = asl::internal_error();
|
||||
asl::status s5 = asl::runtime_error();
|
||||
asl::status s6 = asl::internal_error("Oh, no!");
|
||||
asl::status s7 = asl::internal_error("Oh, no!");
|
||||
asl::status s8 = asl::internal_error("Oh, no");
|
||||
asl::status s9 = asl::runtime_error("Oh, no!");
|
||||
const asl::status s1 = asl::ok();
|
||||
const asl::status s2 = asl::ok();
|
||||
const asl::status s3 = asl::internal_error();
|
||||
const asl::status s4 = asl::internal_error();
|
||||
const asl::status s5 = asl::runtime_error();
|
||||
const asl::status s6 = asl::internal_error("Oh, no!");
|
||||
const asl::status s7 = asl::internal_error("Oh, no!");
|
||||
const asl::status s8 = asl::internal_error("Oh, no");
|
||||
const asl::status s9 = asl::runtime_error("Oh, no!");
|
||||
|
||||
ASL_TEST_EXPECT(asl::hash_value(s1) == asl::hash_value(s2));
|
||||
ASL_TEST_EXPECT(asl::hash_value(s3) == asl::hash_value(s4));
|
||||
@ -244,13 +245,13 @@ static_assert(!asl::hashable<asl::status_or<int*>>);
|
||||
|
||||
ASL_TEST(status_or)
|
||||
{
|
||||
asl::status_or<int> s1 = 42;
|
||||
asl::status_or<int> s2 = 42;
|
||||
asl::status_or<int> s3 = 43;
|
||||
asl::status_or<int> s4 = asl::runtime_error();
|
||||
asl::status_or<int> s5 = asl::runtime_error();
|
||||
asl::status_or<int> s6 = asl::runtime_error("Hello");
|
||||
asl::status_or<int> s7 = asl::runtime_error("Hello");
|
||||
const asl::status_or<int> s1 = 42;
|
||||
const asl::status_or<int> s2 = 42;
|
||||
const asl::status_or<int> s3 = 43;
|
||||
const asl::status_or<int> s4 = asl::runtime_error();
|
||||
const asl::status_or<int> s5 = asl::runtime_error();
|
||||
const asl::status_or<int> s6 = asl::runtime_error("Hello");
|
||||
const asl::status_or<int> s7 = asl::runtime_error("Hello");
|
||||
|
||||
ASL_TEST_EXPECT(asl::hash_value(s1) == asl::hash_value(s2));
|
||||
ASL_TEST_EXPECT(asl::hash_value(s4) == asl::hash_value(s5));
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
|
||||
void write(asl::span<const asl::byte> s) override
|
||||
{
|
||||
fwrite(s.data(), 1, static_cast<size_t>(s.size()), m_handle);
|
||||
(void)fwrite(s.data(), 1, static_cast<size_t>(s.size()), m_handle);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,14 +3,22 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "asl/logging/logging.hpp"
|
||||
|
||||
#include "asl/containers/intrusive_list.hpp"
|
||||
#include "asl/formatting/format.hpp"
|
||||
#include "asl/io/print.hpp"
|
||||
#include "asl/io/writer.hpp"
|
||||
#include "asl/strings/string_builder.hpp"
|
||||
#include "asl/strings/string_view.hpp"
|
||||
#include "asl/types/span.hpp"
|
||||
|
||||
// @Todo Don't use internal get_stdout_writer, make console module
|
||||
|
||||
// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
|
||||
static asl::log::DefaultLogger<asl::Writer*> g_default_logger{asl::print_internals::get_stdout_writer()};
|
||||
|
||||
// @Todo Protect the loggers list being a mutex
|
||||
// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
|
||||
static asl::IntrusiveList<asl::log::Logger> g_loggers(&g_default_logger);
|
||||
|
||||
void asl::log::register_logger(Logger* logger)
|
||||
@ -56,7 +64,7 @@ void asl::log::log_inner(
|
||||
StringWriter msg_writer{};
|
||||
asl::format_internals::format(&msg_writer, fmt, args);
|
||||
|
||||
message m{
|
||||
const message m{
|
||||
.level = l,
|
||||
.message = msg_writer.as_string_view(),
|
||||
.location = sl,
|
||||
|
@ -48,7 +48,11 @@ class DefaultLogger : public DefaultLoggerBase
|
||||
W m_writer;
|
||||
|
||||
public:
|
||||
explicit constexpr DefaultLogger(W&& writer) : m_writer{ASL_FWD(writer)} {}
|
||||
template<typename U>
|
||||
explicit constexpr DefaultLogger(U&& writer)
|
||||
requires constructible_from<W, U&&>
|
||||
: m_writer{std::forward<U>(writer)}
|
||||
{}
|
||||
|
||||
constexpr void log(const message& m) override
|
||||
{
|
||||
@ -74,7 +78,7 @@ void log(level l, const source_location& sl, string_view fmt, const Args&... arg
|
||||
}
|
||||
else
|
||||
{
|
||||
format_internals::type_erased_arg type_erased_args[] = {
|
||||
const format_internals::type_erased_arg type_erased_args[] = {
|
||||
format_internals::type_erased_arg(args)...
|
||||
};
|
||||
log_inner(l, fmt, type_erased_args, sl);
|
||||
|
@ -2,10 +2,11 @@
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "asl/logging/logging.hpp"
|
||||
#include "asl/testing/testing.hpp"
|
||||
#include "asl/strings/string_builder.hpp"
|
||||
#include "asl/base/defer.hpp"
|
||||
#include "asl/logging/logging.hpp"
|
||||
#include "asl/strings/string_builder.hpp"
|
||||
#include "asl/strings/string_view.hpp"
|
||||
#include "asl/testing/testing.hpp"
|
||||
|
||||
ASL_TEST(log)
|
||||
{
|
||||
@ -27,6 +28,6 @@ ASL_TEST(custom_writer)
|
||||
ASL_LOG_INFO("Hello");
|
||||
auto sv = string_writer.as_string_view();
|
||||
|
||||
ASL_TEST_EXPECT(sv == "[ INFO ] asl/logging/logging_tests.cpp:27: Hello\n");
|
||||
ASL_TEST_EXPECT(sv == "[ INFO ] asl/logging/logging_tests.cpp:28: Hello\n");
|
||||
}
|
||||
|
||||
|
@ -9,16 +9,27 @@ package(
|
||||
cc_library(
|
||||
name = "memory",
|
||||
hdrs = [
|
||||
"allocator.hpp",
|
||||
"layout.hpp",
|
||||
"memory.hpp",
|
||||
],
|
||||
srcs = [
|
||||
"allocator.cpp",
|
||||
],
|
||||
deps = [
|
||||
"//asl/base",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "allocator",
|
||||
hdrs = [
|
||||
"allocator.hpp",
|
||||
],
|
||||
srcs = [
|
||||
"allocator.cpp",
|
||||
],
|
||||
deps = [
|
||||
"//asl/base",
|
||||
"//asl/memory",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
|
@ -37,7 +37,7 @@ template<typename T>
|
||||
T* alloc_new(allocator auto& a, auto&&... args)
|
||||
{
|
||||
void* ptr = a.alloc(layout::of<T>());
|
||||
return construct_at<T>(ptr, ASL_FWD(args)...);
|
||||
return construct_at<T>(ptr, std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -50,13 +50,15 @@ void alloc_delete(allocator auto& a, T* ptr)
|
||||
template<typename T>
|
||||
constexpr T* alloc_new_default(auto&&... args)
|
||||
{
|
||||
return alloc_new<T>(DefaultAllocator{}, ASL_FWD(args)...);
|
||||
DefaultAllocator allocator{};
|
||||
return alloc_new<T>(allocator, std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void alloc_delete_default(T* ptr)
|
||||
{
|
||||
alloc_delete(DefaultAllocator{}, ptr);
|
||||
DefaultAllocator allocator{};
|
||||
alloc_delete(allocator, ptr);
|
||||
}
|
||||
|
||||
} // namespace asl
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "asl/base/utility.hpp"
|
||||
#include "asl/memory/layout.hpp"
|
||||
|
||||
constexpr void* operator new(size_t, void* ptr)
|
||||
constexpr void* operator new(size_t, void* ptr) noexcept
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
@ -17,6 +17,17 @@ constexpr void* operator new(size_t, void* ptr)
|
||||
namespace asl
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T* address_of(T& obj)
|
||||
{
|
||||
return __builtin_addressof(obj);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void address_of(const T&& obj) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr isize_t memcmp(const void* a, const void* b, isize_t size)
|
||||
{
|
||||
return __builtin_memcmp(a, b, static_cast<size_t>(size));
|
||||
@ -41,7 +52,7 @@ template<typename T, typename... Args>
|
||||
constexpr T* construct_at(void* ptr, Args&&... args)
|
||||
requires constructible_from<T, Args&&...>
|
||||
{
|
||||
return new (ptr) T{ ASL_FWD(args)... };
|
||||
return new (ptr) T{ std::forward<Args>(args)... }; // NOLINT(*-owning-memory)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -112,7 +123,7 @@ constexpr void relocate_uninit_n(T* to, T* from, isize_t n)
|
||||
for (isize_t i = 0; i < n; ++i)
|
||||
{
|
||||
// NOLINTNEXTLINE(*-pointer-arithmetic)
|
||||
construct_at<T>(to + i, ASL_MOVE(from[i]));
|
||||
construct_at<T>(to + i, std::move(from[i]));
|
||||
}
|
||||
destroy_n(from, n);
|
||||
}
|
||||
@ -131,7 +142,7 @@ constexpr void relocate_assign_n(T* to, T* from, isize_t n)
|
||||
for (isize_t i = 0; i < n; ++i)
|
||||
{
|
||||
// NOLINTNEXTLINE(*-pointer-arithmetic)
|
||||
to[i] = ASL_MOVE(from[i]);
|
||||
to[i] = std::move(from[i]);
|
||||
}
|
||||
destroy_n(from, n);
|
||||
}
|
||||
|
@ -11,12 +11,12 @@ namespace asl
|
||||
{
|
||||
|
||||
template<allocator Allocator = DefaultAllocator>
|
||||
class string
|
||||
class string : protected buffer<char, Allocator>
|
||||
{
|
||||
buffer<char, Allocator> m_buffer;
|
||||
using Base = buffer<char, Allocator>;
|
||||
|
||||
explicit constexpr string(buffer<char, Allocator>&& buffer) :
|
||||
m_buffer{ASL_MOVE(buffer)}
|
||||
explicit constexpr string(buffer<char, Allocator>&& b) :
|
||||
Base{std::move(b)}
|
||||
{}
|
||||
|
||||
template<allocator A>
|
||||
@ -24,38 +24,36 @@ class string
|
||||
|
||||
public:
|
||||
constexpr string() requires default_constructible<Allocator> = default;
|
||||
explicit constexpr string(Allocator allocator) : m_buffer{ASL_MOVE(allocator)} {}
|
||||
|
||||
// NOLINTNEXTLINE(*-explicit-conversions)
|
||||
explicit constexpr string(Allocator allocator)
|
||||
: Base{std::move(allocator)}
|
||||
{}
|
||||
|
||||
// NOLINTNEXTLINE(*explicit*)
|
||||
constexpr string(string_view sv)
|
||||
requires default_constructible<Allocator>
|
||||
: m_buffer{sv.as_span()}
|
||||
: Base{sv.as_span()}
|
||||
{}
|
||||
|
||||
constexpr string(string_view sv, Allocator allocator)
|
||||
: m_buffer{sv.as_span(), ASL_MOVE(allocator)}
|
||||
: Base{sv.as_span(), std::move(allocator)}
|
||||
{}
|
||||
|
||||
constexpr ~string() = default;
|
||||
using Base::size;
|
||||
|
||||
constexpr string(const string&) requires copy_constructible<Allocator> = default;
|
||||
constexpr string(string&&) = default;
|
||||
using Base::is_empty;
|
||||
|
||||
constexpr string& operator=(const string&) requires copy_assignable<Allocator> = default;
|
||||
constexpr string& operator=(string&&) = default;
|
||||
[[nodiscard]] constexpr const char* data() const { return Base::data(); }
|
||||
|
||||
constexpr isize_t size() const { return m_buffer.size(); }
|
||||
constexpr const char* data() const { return m_buffer.data(); }
|
||||
|
||||
// NOLINTNEXTLINE(*-explicit-conversions)
|
||||
// NOLINTNEXTLINE(*explicit*)
|
||||
constexpr operator string_view() const
|
||||
{
|
||||
return as_string_view();
|
||||
}
|
||||
|
||||
constexpr string_view as_string_view() const
|
||||
[[nodiscard]] constexpr string_view as_string_view() const
|
||||
{
|
||||
auto span = m_buffer.as_span();
|
||||
auto span = Base::as_span();
|
||||
return string_view{span.data(), span.size()};
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ class StringBuilder
|
||||
|
||||
public:
|
||||
constexpr StringBuilder() requires default_constructible<Allocator> = default;
|
||||
explicit constexpr StringBuilder(Allocator allocator) : m_buffer{ASL_MOVE(allocator)} {}
|
||||
explicit constexpr StringBuilder(Allocator allocator) : m_buffer{std::move(allocator)} {}
|
||||
|
||||
constexpr ~StringBuilder() = default;
|
||||
|
||||
@ -30,7 +30,7 @@ public:
|
||||
constexpr StringBuilder& operator=(const StringBuilder&) requires copy_assignable<Allocator> = default;
|
||||
constexpr StringBuilder& operator=(StringBuilder&&) = default;
|
||||
|
||||
constexpr string_view as_string_view() const
|
||||
[[nodiscard]] constexpr string_view as_string_view() const
|
||||
{
|
||||
auto span = m_buffer.as_span();
|
||||
return string_view{span.data(), span.size()};
|
||||
@ -44,23 +44,23 @@ public:
|
||||
auto push(this auto&& self, string_view sv) -> decltype(self)
|
||||
requires (!is_const<un_ref_t<decltype(self)>>)
|
||||
{
|
||||
isize_t old_size = self.m_buffer.size();
|
||||
self.m_buffer.resize_zero(old_size + sv.size());
|
||||
const isize_t old_size = self.m_buffer.size();
|
||||
self.m_buffer.resize_uninit(old_size + sv.size());
|
||||
// NOLINTNEXTLINE(*-pointer-arithmetic)
|
||||
asl::memcpy(self.m_buffer.data() + old_size, sv.data(), sv.size());
|
||||
return ASL_FWD(self);
|
||||
return std::forward<decltype(self)>(self);
|
||||
}
|
||||
|
||||
auto push(this auto&& self, char c) -> decltype(self)
|
||||
requires (!is_const<un_ref_t<decltype(self)>>)
|
||||
{
|
||||
self.m_buffer.push(c);
|
||||
return ASL_FWD(self);
|
||||
return std::forward<decltype(self)>(self);
|
||||
}
|
||||
|
||||
string<Allocator> finish() &&
|
||||
{
|
||||
return string<Allocator>{ASL_MOVE(m_buffer)};
|
||||
return string<Allocator>{std::move(m_buffer)};
|
||||
}
|
||||
|
||||
template<allocator StringAllocator = Allocator>
|
||||
@ -73,7 +73,7 @@ public:
|
||||
template<allocator StringAllocator = Allocator>
|
||||
string<StringAllocator> as_string(Allocator allocator) const
|
||||
{
|
||||
return string<StringAllocator>{as_string_view(), ASL_MOVE(allocator)};
|
||||
return string<StringAllocator>{as_string_view(), std::move(allocator)};
|
||||
}
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ class StringWriter : public asl::Writer
|
||||
|
||||
public:
|
||||
constexpr StringWriter() requires default_constructible<Allocator> = default;
|
||||
explicit constexpr StringWriter(Allocator allocator) : m_builder{ASL_MOVE(allocator)} {}
|
||||
explicit constexpr StringWriter(Allocator allocator) : m_builder{std::move(allocator)} {}
|
||||
|
||||
constexpr ~StringWriter() override = default;
|
||||
|
||||
@ -98,17 +98,18 @@ public:
|
||||
|
||||
void write(span<const byte> str) override
|
||||
{
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
m_builder.push(string_view{reinterpret_cast<const char*>(str.data()), str.size()});
|
||||
}
|
||||
|
||||
constexpr string_view as_string_view() const
|
||||
[[nodiscard]] constexpr string_view as_string_view() const
|
||||
{
|
||||
return m_builder.as_string_view();
|
||||
}
|
||||
|
||||
string<Allocator> finish() &&
|
||||
{
|
||||
return ASL_MOVE(m_builder).finish();
|
||||
return std::move(m_builder).finish();
|
||||
}
|
||||
|
||||
template<allocator StringAllocator = Allocator>
|
||||
@ -121,7 +122,7 @@ public:
|
||||
template<allocator StringAllocator = Allocator>
|
||||
string<StringAllocator> as_string(Allocator allocator) const
|
||||
{
|
||||
return m_builder.as_string(ASL_MOVE(allocator));
|
||||
return m_builder.as_string(std::move(allocator));
|
||||
}
|
||||
};
|
||||
|
||||
@ -133,15 +134,15 @@ string<Allocator> format_to_string(string_view fmt, const formattable auto&... a
|
||||
{
|
||||
StringWriter writer{};
|
||||
format(&writer, fmt, args...);
|
||||
return ASL_MOVE(writer).finish();
|
||||
return std::move(writer).finish();
|
||||
}
|
||||
|
||||
template<allocator Allocator = DefaultAllocator>
|
||||
string<Allocator> format_to_string(Allocator allocator, string_view fmt, const formattable auto&... args)
|
||||
{
|
||||
StringWriter writer{ASL_MOVE(allocator)};
|
||||
StringWriter writer{std::move(allocator)};
|
||||
format(&writer, fmt, args...);
|
||||
return ASL_MOVE(writer).finish();
|
||||
return std::move(writer).finish();
|
||||
}
|
||||
|
||||
} // namespace asl
|
||||
|
@ -14,13 +14,13 @@ ASL_TEST(string_builder)
|
||||
|
||||
ASL_TEST_EXPECT(b.as_string_view() == "abcdefg");
|
||||
|
||||
asl::string s = b.as_string();
|
||||
const asl::string s = b.as_string();
|
||||
ASL_TEST_EXPECT(s == "abcdefg");
|
||||
}
|
||||
|
||||
ASL_TEST(string_builder_rvalue)
|
||||
{
|
||||
asl::string s = asl::StringBuilder{}.push('a').push("bcdef").push('g').finish();
|
||||
const asl::string s = asl::StringBuilder{}.push('a').push("bcdef").push('g').finish();
|
||||
|
||||
ASL_TEST_EXPECT(s == "abcdefg");
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
ASL_TEST(default)
|
||||
{
|
||||
asl::string s;
|
||||
const asl::string s;
|
||||
ASL_TEST_ASSERT(s.size() == 0);
|
||||
ASL_TEST_ASSERT(s.as_string_view().size() == 0);
|
||||
ASL_TEST_ASSERT(s == ""_sv);
|
||||
@ -17,7 +17,7 @@ ASL_TEST(default)
|
||||
|
||||
ASL_TEST(from_string_view)
|
||||
{
|
||||
asl::string s = "hello"_sv;
|
||||
const asl::string s = "hello"_sv;
|
||||
ASL_TEST_ASSERT(s.size() == 5);
|
||||
ASL_TEST_ASSERT(s == "hello"_sv);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace asl
|
||||
{
|
||||
|
||||
// NOLINTBEGIN(*-convert-member-functions-to-static)
|
||||
class string_view
|
||||
{
|
||||
const char* m_data{};
|
||||
@ -20,7 +21,7 @@ class string_view
|
||||
public:
|
||||
constexpr string_view() = default;
|
||||
|
||||
constexpr string_view(nullptr_t) : string_view() {} // NOLINT(*-explicit-conversions)
|
||||
constexpr string_view(nullptr_t) : string_view() {} // NOLINT(*explicit*)
|
||||
|
||||
constexpr string_view(const char* data, isize_t size)
|
||||
: m_data{data}
|
||||
@ -28,9 +29,9 @@ public:
|
||||
{}
|
||||
|
||||
template<isize_t kSize>
|
||||
constexpr string_view(const char (&str)[kSize]) // NOLINT(*-explicit-conversions)
|
||||
constexpr string_view(const char (&str)[kSize]) // NOLINT(*explicit*)
|
||||
requires (kSize >= 1)
|
||||
: m_data{str}
|
||||
: m_data{static_cast<const char*>(str)}
|
||||
, m_size{kSize - 1}
|
||||
{
|
||||
ASL_ASSERT(m_data[kSize - 1] == '\0'); // NOLINT(*-pointer-arithmetic)
|
||||
@ -38,7 +39,7 @@ public:
|
||||
|
||||
static constexpr string_view from_zstr(const char* str)
|
||||
{
|
||||
return string_view(str, asl::strlen(str));
|
||||
return {str, asl::strlen(str)};
|
||||
}
|
||||
|
||||
constexpr string_view(const string_view&) = default;
|
||||
@ -49,51 +50,69 @@ public:
|
||||
|
||||
~string_view() = default;
|
||||
|
||||
constexpr isize_t size() const { return m_size; }
|
||||
|
||||
constexpr bool is_empty() const { return m_size == 0; }
|
||||
|
||||
constexpr const char* data() const { return m_data; }
|
||||
|
||||
constexpr contiguous_iterator<const char> begin() const { return contiguous_iterator{m_data}; }
|
||||
|
||||
// NOLINTNEXTLINE(*-pointer-arithmetic)
|
||||
constexpr contiguous_iterator<const char> end() const { return contiguous_iterator{m_data + m_size}; }
|
||||
|
||||
constexpr span<const char> as_span() const { return span<const char>(m_data, m_size); }
|
||||
|
||||
constexpr char operator[](isize_t i) const
|
||||
[[nodiscard]] constexpr isize_t size(this string_view self)
|
||||
{
|
||||
ASL_ASSERT(i >= 0 && i < m_size);
|
||||
return m_data[i]; // NOLINT(*-pointer-arithmetic)
|
||||
return self.m_size;
|
||||
}
|
||||
|
||||
constexpr string_view substr(isize_t offset, isize_t size) const
|
||||
[[nodiscard]] constexpr bool is_empty(this string_view self)
|
||||
{
|
||||
ASL_ASSERT(offset >= 0 && size >= 0 && offset + size <= m_size);
|
||||
return string_view{m_data + offset, size}; // NOLINT(*-pointer-arithmetic)
|
||||
return self.m_size == 0;
|
||||
}
|
||||
|
||||
constexpr string_view substr(isize_t offset) const
|
||||
[[nodiscard]] constexpr const char* data(this string_view self)
|
||||
{
|
||||
ASL_ASSERT(offset >= 0 && offset <= m_size);
|
||||
return string_view{m_data + offset, m_size - offset}; // NOLINT(*-pointer-arithmetic)
|
||||
return self.m_data;
|
||||
}
|
||||
|
||||
constexpr string_view first(isize_t size) const
|
||||
[[nodiscard]] constexpr contiguous_iterator<const char> begin(this string_view self)
|
||||
{
|
||||
return substr(0, size);
|
||||
return contiguous_iterator{self.m_data};
|
||||
}
|
||||
|
||||
constexpr string_view last(isize_t size) const
|
||||
[[nodiscard]] constexpr contiguous_iterator<const char> end(this string_view self)
|
||||
{
|
||||
return substr(m_size - size);
|
||||
// NOLINTNEXTLINE(*-pointer-arithmetic)
|
||||
return contiguous_iterator{self.m_data + self.m_size};
|
||||
}
|
||||
|
||||
constexpr bool operator==(string_view other) const
|
||||
[[nodiscard]] constexpr span<const char> as_span(this string_view self)
|
||||
{
|
||||
if (m_size != other.m_size) { return false; }
|
||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
||||
return {self.m_data, self.m_size};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr char operator[](this string_view self, isize_t i)
|
||||
{
|
||||
ASL_ASSERT(i >= 0 && i < self.m_size);
|
||||
return self.m_data[i]; // NOLINT(*-pointer-arithmetic)
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr string_view substr(this string_view self, isize_t offset, isize_t size)
|
||||
{
|
||||
ASL_ASSERT(offset >= 0 && size >= 0 && offset + size <= self.m_size);
|
||||
return string_view{self.m_data + offset, size}; // NOLINT(*-pointer-arithmetic)
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr string_view substr(this string_view self, isize_t offset)
|
||||
{
|
||||
ASL_ASSERT(offset >= 0 && offset <= self.m_size);
|
||||
return string_view{self.m_data + offset, self.m_size - offset}; // NOLINT(*-pointer-arithmetic)
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr string_view first(this string_view self, isize_t size)
|
||||
{
|
||||
return self.substr(0, size);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr string_view last(this string_view self, isize_t size)
|
||||
{
|
||||
return self.substr(self.m_size - size);
|
||||
}
|
||||
|
||||
constexpr bool operator==(this string_view self, string_view other)
|
||||
{
|
||||
if (self.m_size != other.m_size) { return false; }
|
||||
return asl::memcmp(self.m_data, other.m_data, self.m_size) == 0;
|
||||
}
|
||||
|
||||
template<typename H>
|
||||
@ -102,10 +121,11 @@ public:
|
||||
return H::combine(H::combine_contiguous(h, as_bytes(sv.as_span())), sv.size());
|
||||
}
|
||||
};
|
||||
// NOLINTEND(*-convert-member-functions-to-static)
|
||||
|
||||
} // namespace asl
|
||||
|
||||
constexpr asl::string_view operator ""_sv(const char* s, size_t len)
|
||||
{
|
||||
return asl::string_view(s, static_cast<isize_t>(len));
|
||||
return {s, static_cast<isize_t>(len)};
|
||||
}
|
||||
|
@ -10,20 +10,20 @@ static_assert(asl::trivially_copy_constructible<asl::string_view>);
|
||||
|
||||
ASL_TEST(default)
|
||||
{
|
||||
asl::string_view s1;
|
||||
const asl::string_view s1;
|
||||
ASL_TEST_EXPECT(s1.is_empty());
|
||||
|
||||
asl::string_view s2 = nullptr;
|
||||
const asl::string_view s2 = nullptr;
|
||||
ASL_TEST_EXPECT(s2.is_empty());
|
||||
}
|
||||
|
||||
ASL_TEST(from_literal)
|
||||
{
|
||||
asl::string_view s1 = "Hello"_sv;
|
||||
const asl::string_view s1 = "Hello"_sv;
|
||||
ASL_TEST_ASSERT(s1.size() == 5);
|
||||
ASL_TEST_EXPECT(asl::memcmp(s1.data(), "Hello", 5) == 0);
|
||||
|
||||
asl::string_view s2 = ""_sv;
|
||||
const asl::string_view s2 = ""_sv;
|
||||
ASL_TEST_EXPECT(s2.is_empty());
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ ASL_TEST(from_zstr)
|
||||
|
||||
ASL_TEST(substr1)
|
||||
{
|
||||
asl::string_view s1 = "abcd";
|
||||
const asl::string_view s1 = "abcd";
|
||||
|
||||
asl::string_view s2 = s1.substr(0);
|
||||
ASL_TEST_ASSERT(s2.size() == 4);
|
||||
@ -63,7 +63,7 @@ ASL_TEST(substr1)
|
||||
|
||||
ASL_TEST(substr2)
|
||||
{
|
||||
asl::string_view s1 = "abcd";
|
||||
const asl::string_view s1 = "abcd";
|
||||
|
||||
asl::string_view s2 = s1.substr(0, 4);
|
||||
ASL_TEST_ASSERT(s2.size() == 4);
|
||||
@ -82,7 +82,7 @@ ASL_TEST(substr2)
|
||||
|
||||
ASL_TEST(first)
|
||||
{
|
||||
asl::string_view s1 = "abcd";
|
||||
const asl::string_view s1 = "abcd";
|
||||
|
||||
asl::string_view s2 = s1.first(0);
|
||||
ASL_TEST_ASSERT(s2.size() == 0);
|
||||
@ -98,7 +98,7 @@ ASL_TEST(first)
|
||||
|
||||
ASL_TEST(last)
|
||||
{
|
||||
asl::string_view s1 = "abcd";
|
||||
const asl::string_view s1 = "abcd";
|
||||
|
||||
asl::string_view s2 = s1.last(0);
|
||||
ASL_TEST_ASSERT(s2.size() == 0);
|
||||
|
@ -4,37 +4,51 @@
|
||||
|
||||
#include "asl/testing/testing.hpp"
|
||||
|
||||
#include "asl/base/assert.hpp"
|
||||
#include "asl/base/meta.hpp"
|
||||
#include "asl/base/utility.hpp"
|
||||
#include "asl/io/print.hpp"
|
||||
|
||||
static asl::testing::Test* g_head = nullptr;
|
||||
static asl::testing::Test* g_tail = nullptr;
|
||||
namespace
|
||||
{
|
||||
|
||||
struct TestingState
|
||||
{
|
||||
asl::testing::Test* head = nullptr;
|
||||
asl::testing::Test* tail = nullptr;
|
||||
|
||||
bool current_test_fail = false;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
|
||||
static TestingState g_state{};
|
||||
|
||||
void asl::testing::register_test(Test* test)
|
||||
{
|
||||
if (g_head == nullptr && g_tail == nullptr)
|
||||
if (g_state.head == nullptr && g_state.tail == nullptr)
|
||||
{
|
||||
g_head = test;
|
||||
g_tail = test;
|
||||
g_state.head = test;
|
||||
g_state.tail = test;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_tail->m_next = test;
|
||||
test->m_prev = asl::exchange(g_tail, test);
|
||||
g_state.tail->m_next = test;
|
||||
test->m_prev = asl::exchange(g_state.tail, test);
|
||||
}
|
||||
}
|
||||
|
||||
static bool g_current_test_fail = false;
|
||||
|
||||
void asl::testing::report_failure(const char* msg, const asl::source_location& sl)
|
||||
{
|
||||
asl::eprint("--------------------------------------------------------------\n");
|
||||
asl::eprint("Test assertion failed at {}, line {}:\n", sl.file, sl.line);
|
||||
asl::eprint(" {}\n", msg);
|
||||
asl::eprint("--------------------------------------------------------------\n");
|
||||
g_current_test_fail = true;
|
||||
g_state.current_test_fail = true;
|
||||
}
|
||||
|
||||
static void report_assert_failure(const char* msg, const asl::source_location& sl, void*)
|
||||
static void report_assert_failure(const char* msg, const asl::source_location& sl, void* /* userdata */)
|
||||
{
|
||||
asl::eprint("------------------------------------------------------------\n");
|
||||
asl::eprint("Assertion failure at {}, line {}:\n", sl.file, sl.line);
|
||||
@ -55,14 +69,14 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
|
||||
|
||||
asl::testing::Test* failed_head = nullptr;
|
||||
|
||||
for (auto* it = g_head; it != nullptr; it = it->m_next)
|
||||
for (auto* it = g_state.head; it != nullptr; it = it->m_next)
|
||||
{
|
||||
asl::eprint(GREEN("[ RUN ]") " {}\n", it->m_case_name);
|
||||
|
||||
g_current_test_fail = false;
|
||||
g_state.current_test_fail = false;
|
||||
it->m_fn();
|
||||
|
||||
if (!g_current_test_fail)
|
||||
if (!g_state.current_test_fail)
|
||||
{
|
||||
asl::eprint(GREEN("[ OK ]") " {}\n", it->m_case_name);
|
||||
pass += 1;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "asl/base/utility.hpp"
|
||||
#include "asl/base/meta.hpp"
|
||||
|
||||
namespace asl::testing
|
||||
{
|
||||
@ -35,7 +35,8 @@ struct Test
|
||||
} // namespace asl::testing
|
||||
|
||||
#define ASL_TEST(CASE) \
|
||||
static void asl_test_fn_##CASE(); \
|
||||
static void asl_test_fn_##CASE(); /* NOLINT */ \
|
||||
/* NOLINTNEXTLINE */ \
|
||||
static ::asl::testing::Test asl_test_##CASE( \
|
||||
#CASE, \
|
||||
asl_test_fn_##CASE); \
|
||||
@ -45,6 +46,6 @@ struct Test
|
||||
if (EXPR) {} \
|
||||
else { ::asl::testing::report_failure(#EXPR); return; }
|
||||
|
||||
#define ASL_TEST_EXPECT(EXPR) \
|
||||
if (EXPR) {} \
|
||||
else { ::asl::testing::report_failure(#EXPR); }
|
||||
#define ASL_TEST_EXPECT(...) \
|
||||
if (__VA_ARGS__) {} \
|
||||
else { ::asl::testing::report_failure(#__VA_ARGS__); }
|
||||
|
@ -14,6 +14,7 @@ cc_library(
|
||||
deps = [
|
||||
"//asl/base",
|
||||
"//asl/memory",
|
||||
"//asl/memory:allocator",
|
||||
"//asl/hashing",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
@ -78,6 +79,51 @@ cc_library(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "function",
|
||||
hdrs = [
|
||||
"function.hpp",
|
||||
],
|
||||
deps = [
|
||||
"//asl/base",
|
||||
"//asl/memory",
|
||||
"//asl/memory:allocator",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "function_ref",
|
||||
hdrs = [
|
||||
"function_ref.hpp",
|
||||
],
|
||||
deps = [
|
||||
"//asl/base",
|
||||
"//asl/memory",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "function_tests",
|
||||
srcs = ["function_tests.cpp"],
|
||||
deps = [
|
||||
"//asl/tests:utils",
|
||||
"//asl/testing",
|
||||
"//asl/types:function",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "function_ref_tests",
|
||||
srcs = ["function_ref_tests.cpp"],
|
||||
deps = [
|
||||
"//asl/tests:utils",
|
||||
"//asl/testing",
|
||||
"//asl/types:function_ref",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "box_tests",
|
||||
srcs = ["box_tests.cpp"],
|
||||
|
@ -29,21 +29,21 @@ public:
|
||||
|
||||
constexpr box(T* ptr, Allocator alloc)
|
||||
: m_ptr{ptr}
|
||||
, m_alloc{ASL_MOVE(alloc)}
|
||||
, m_alloc{std::move(alloc)}
|
||||
{
|
||||
ASL_ASSERT(m_ptr != nullptr);
|
||||
}
|
||||
|
||||
constexpr box(box&& other)
|
||||
: m_ptr{exchange(other.m_ptr, nullptr)}
|
||||
, m_alloc{ASL_MOVE(other.m_alloc)}
|
||||
, m_alloc{std::move(other.m_alloc)}
|
||||
{}
|
||||
|
||||
template<is_object U>
|
||||
requires convertible_from<T*, U*>
|
||||
constexpr box(box<U, Allocator>&& other) // NOLINT(*-explicit-conversions)
|
||||
requires convertible_to<U*, T*>
|
||||
constexpr box(box<U, Allocator>&& other) // NOLINT(*explicit*,*-not-moved)
|
||||
: m_ptr{exchange(other.m_ptr, nullptr)}
|
||||
, m_alloc{ASL_MOVE(other.m_alloc)}
|
||||
, m_alloc{std::move(other.m_alloc)}
|
||||
{}
|
||||
|
||||
constexpr box& operator=(box&& other)
|
||||
@ -53,7 +53,7 @@ public:
|
||||
if (m_ptr != nullptr) { reset(); }
|
||||
|
||||
m_ptr = exchange(other.m_ptr, nullptr);
|
||||
m_alloc = ASL_MOVE(other.m_alloc);
|
||||
m_alloc = std::move(other.m_alloc);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -76,18 +76,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T* get() const { return m_ptr; }
|
||||
|
||||
constexpr T& operator*() const
|
||||
constexpr auto* get(this auto&& self)
|
||||
{
|
||||
ASL_ASSERT(m_ptr != nullptr);
|
||||
return *m_ptr;
|
||||
return self.m_ptr;
|
||||
}
|
||||
|
||||
constexpr T* operator->() const
|
||||
constexpr auto&& operator*(this auto&& self)
|
||||
{
|
||||
ASL_ASSERT(m_ptr != nullptr);
|
||||
return m_ptr;
|
||||
ASL_ASSERT(self.m_ptr != nullptr);
|
||||
return std::forward_like<decltype(self)&&>(*self.m_ptr);
|
||||
}
|
||||
|
||||
constexpr auto* operator->(this auto&& self)
|
||||
{
|
||||
ASL_ASSERT(self.m_ptr != nullptr);
|
||||
return self.m_ptr;
|
||||
}
|
||||
|
||||
constexpr bool operator==(niche_t) const
|
||||
@ -99,7 +102,7 @@ public:
|
||||
requires hashable<T>
|
||||
friend H AslHashValue(H h, const box& b)
|
||||
{
|
||||
return H::combine(ASL_MOVE(h), *b);
|
||||
return H::combine(std::move(h), *b);
|
||||
}
|
||||
|
||||
template<is_object U, allocator A>
|
||||
@ -114,8 +117,8 @@ constexpr box<T, Allocator> make_box_in(Allocator allocator, Args&&... args)
|
||||
requires constructible_from<T, Args&&...>
|
||||
{
|
||||
void* raw_ptr = allocator.alloc(layout::of<T>());
|
||||
auto* ptr = construct_at<T>(raw_ptr, ASL_FWD(args)...);
|
||||
return box(ptr, ASL_MOVE(allocator));
|
||||
auto* ptr = construct_at<T>(raw_ptr, std::forward<Args>(args)...);
|
||||
return box(ptr, std::move(allocator));
|
||||
}
|
||||
|
||||
template<is_object T, allocator Allocator = DefaultAllocator, typename... Args>
|
||||
@ -124,14 +127,14 @@ constexpr box<T, Allocator> make_box(Args&&... args)
|
||||
{
|
||||
Allocator allocator{};
|
||||
void* raw_ptr = allocator.alloc(layout::of<T>());
|
||||
auto* ptr = construct_at<T>(raw_ptr, ASL_FWD(args)...);
|
||||
return box<T>(ptr, ASL_MOVE(allocator));
|
||||
auto* ptr = construct_at<T>(raw_ptr, std::forward<Args>(args)...);
|
||||
return box<T>(ptr, std::move(allocator));
|
||||
}
|
||||
|
||||
template<is_object T, allocator A>
|
||||
constexpr T* leak(box<T, A>&& b)
|
||||
{
|
||||
return exchange(b.m_ptr, nullptr);
|
||||
return exchange(std::move(b).m_ptr, nullptr);
|
||||
}
|
||||
|
||||
} // namespace asl
|
||||
|
@ -23,7 +23,7 @@ ASL_TEST(destructor)
|
||||
ASL_TEST_ASSERT(!d);
|
||||
|
||||
|
||||
auto box3 = ASL_MOVE(box);
|
||||
auto box3 = std::move(box);
|
||||
ASL_TEST_ASSERT(!d);
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ ASL_TEST(value)
|
||||
auto b = asl::make_box<int>(24);
|
||||
ASL_TEST_EXPECT(*b == 24);
|
||||
|
||||
auto b2 = ASL_MOVE(b);
|
||||
auto b2 = std::move(b);
|
||||
ASL_TEST_EXPECT(*b2 == 24);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ ASL_TEST(ptr)
|
||||
auto b = asl::make_box<int>(24);
|
||||
auto* ptr1 = b.get();
|
||||
|
||||
auto b2 = ASL_MOVE(b);
|
||||
auto b2 = std::move(b);
|
||||
auto* ptr2 = b2.get();
|
||||
ASL_TEST_EXPECT(ptr1 == ptr2);
|
||||
}
|
||||
@ -81,7 +81,7 @@ ASL_TEST(niche)
|
||||
ASL_TEST_EXPECT(destroyed);
|
||||
}
|
||||
|
||||
class Base
|
||||
class Base // NOLINT
|
||||
{
|
||||
public:
|
||||
virtual ~Base() = default;
|
||||
@ -94,10 +94,10 @@ public:
|
||||
int number() override { return 2; }
|
||||
};
|
||||
|
||||
static_assert(asl::convertible_from<asl::box<Base>, asl::box<Derived>>);
|
||||
static_assert(asl::convertible_from<asl::box<Base>, asl::box<Base>>);
|
||||
static_assert(!asl::convertible_from<asl::box<Derived>, asl::box<Base>>);
|
||||
static_assert(!asl::convertible_from<asl::box<int>, asl::box<float>>);
|
||||
static_assert(asl::convertible_to<asl::box<Derived>, asl::box<Base>>);
|
||||
static_assert(asl::convertible_to<asl::box<Base>, asl::box<Base>>);
|
||||
static_assert(!asl::convertible_to<asl::box<Base>, asl::box<Derived>>);
|
||||
static_assert(!asl::convertible_to<asl::box<float>, asl::box<int>>);
|
||||
|
||||
ASL_TEST(derived)
|
||||
{
|
||||
|
288
asl/types/function.hpp
Normal file
288
asl/types/function.hpp
Normal file
@ -0,0 +1,288 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "asl/base/utility.hpp"
|
||||
#include "asl/base/meta.hpp"
|
||||
#include "asl/base/integers.hpp"
|
||||
#include "asl/memory/allocator.hpp"
|
||||
#include "asl/base/functional.hpp"
|
||||
|
||||
namespace asl
|
||||
{
|
||||
|
||||
namespace function_detail
|
||||
{
|
||||
|
||||
static constexpr isize_t kStorageSize = size_of<void*> * 2;
|
||||
|
||||
struct Storage
|
||||
{
|
||||
alignas(align_of<void*>) byte raw[kStorageSize];
|
||||
|
||||
[[nodiscard]]
|
||||
void* get_ptr() const
|
||||
{
|
||||
// NOLINTNEXTLINE(*-const-cast)
|
||||
return const_cast<void*>(static_cast<const void*>(raw));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept can_be_stored_inline =
|
||||
size_of<T> <= size_of<Storage> &&
|
||||
align_of<Storage> % align_of<T> == 0;
|
||||
|
||||
enum class FunctionOp : uint8_t
|
||||
{
|
||||
kDestroyThis,
|
||||
kCopyFromOtherToThisUninit,
|
||||
kMoveFromOtherToThisUninit,
|
||||
};
|
||||
|
||||
template<typename Functor, bool kStoreInline = can_be_stored_inline<Functor>>
|
||||
struct FunctionImplBase
|
||||
{
|
||||
using Allocator = DefaultAllocator;
|
||||
|
||||
template<typename T>
|
||||
static void create(Storage* storage, T&& t)
|
||||
{
|
||||
Allocator allocator{};
|
||||
auto* ptr = alloc_new<Functor>(allocator, std::forward<T>(t));
|
||||
asl::memcpy(storage->get_ptr(), static_cast<void*>(&ptr), size_of<void*>);
|
||||
}
|
||||
|
||||
static Functor** get_functor_ptr(const Storage* storage)
|
||||
{
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
return std::launder(reinterpret_cast<Functor**>(storage->get_ptr()));
|
||||
}
|
||||
|
||||
static Functor* get_functor(const Storage* storage)
|
||||
{
|
||||
return *get_functor_ptr(storage);
|
||||
}
|
||||
|
||||
static void op(Storage* this_storage, Storage* other_storage, FunctionOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
using enum FunctionOp;
|
||||
case kDestroyThis:
|
||||
{
|
||||
Allocator allocator{};
|
||||
alloc_delete(allocator, get_functor(this_storage));
|
||||
break;
|
||||
}
|
||||
case kCopyFromOtherToThisUninit:
|
||||
{
|
||||
create(this_storage, *static_cast<const Functor*>(get_functor(other_storage)));
|
||||
break;
|
||||
}
|
||||
case kMoveFromOtherToThisUninit:
|
||||
{
|
||||
auto* ptr = asl::exchange(*get_functor_ptr(other_storage), nullptr);
|
||||
asl::memcpy(this_storage->get_ptr(), static_cast<void*>(&ptr), size_of<void*>);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Functor>
|
||||
struct FunctionImplBase<Functor, true>
|
||||
{
|
||||
template<typename T>
|
||||
static void create(Storage* storage, T&& t)
|
||||
{
|
||||
new (storage->get_ptr()) Functor(std::forward<T>(t));
|
||||
}
|
||||
|
||||
static Functor* get_functor(const Storage* storage)
|
||||
{
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
return std::launder(reinterpret_cast<Functor*>(storage->get_ptr()));
|
||||
}
|
||||
|
||||
static void op(Storage* this_storage, Storage* other_storage, FunctionOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
using enum FunctionOp;
|
||||
case kDestroyThis:
|
||||
{
|
||||
destroy(get_functor(this_storage));
|
||||
break;
|
||||
}
|
||||
case kCopyFromOtherToThisUninit:
|
||||
{
|
||||
create(this_storage, *static_cast<const Functor*>(get_functor(other_storage)));
|
||||
break;
|
||||
}
|
||||
case kMoveFromOtherToThisUninit:
|
||||
{
|
||||
auto* other_functor = get_functor(other_storage);
|
||||
create(this_storage, std::move(*static_cast<const Functor*>(other_functor)));
|
||||
destroy(other_functor);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Functor, typename R, typename... Args>
|
||||
struct FunctionImpl : FunctionImplBase<Functor>
|
||||
{
|
||||
static R invoke(Args... args, const Storage& storage)
|
||||
{
|
||||
auto* functor = FunctionImplBase<Functor>::get_functor(&storage);
|
||||
return asl::invoke(*functor, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename R, typename... Args>
|
||||
concept valid_functor =
|
||||
copy_constructible<T>
|
||||
&& move_constructible<T>
|
||||
&& invocable<T, Args...>
|
||||
&& same_as<R, invoke_result_t<T, Args...>>;
|
||||
|
||||
} // namespace function_detail
|
||||
|
||||
template<typename T>
|
||||
class function;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
class function<R(Args...)> // NOLINT(*-member-init)
|
||||
{
|
||||
using InvokeFn = R (*)(Args..., const function_detail::Storage&);
|
||||
using OpFn = void (*)(function_detail::Storage*, function_detail::Storage*, function_detail::FunctionOp);
|
||||
|
||||
function_detail::Storage m_storage;
|
||||
InvokeFn m_invoke{};
|
||||
OpFn m_op{};
|
||||
|
||||
void destroy()
|
||||
{
|
||||
if (m_op != nullptr)
|
||||
{
|
||||
(*m_op)(&m_storage, nullptr, function_detail::FunctionOp::kDestroyThis);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
function() = default;
|
||||
|
||||
template<typename T>
|
||||
function(T&& func) // NOLINT(*explicit*,*-member-init)
|
||||
requires (
|
||||
!same_as<function, un_cvref_t<T>>
|
||||
&& function_detail::valid_functor<T, R, Args...>
|
||||
)
|
||||
{
|
||||
using Functor = decay_t<T>;
|
||||
using Impl = function_detail::FunctionImpl<Functor, R, Args...>;
|
||||
|
||||
Impl::create(&m_storage, std::forward<T>(func));
|
||||
m_invoke = &Impl::invoke; // NOLINT(*-member-initializer)
|
||||
m_op = &Impl::op; // NOLINT(*-member-initializer)
|
||||
}
|
||||
|
||||
function(const function& other) // NOLINT(*-member-init)
|
||||
: m_invoke{other.m_invoke}
|
||||
, m_op{other.m_op}
|
||||
{
|
||||
if (m_op != nullptr)
|
||||
{
|
||||
m_op(
|
||||
&m_storage,
|
||||
const_cast<function_detail::Storage*>(&other.m_storage), // NOLINT(*-const-cast)
|
||||
function_detail::FunctionOp::kCopyFromOtherToThisUninit);
|
||||
}
|
||||
}
|
||||
|
||||
function(function&& other) // NOLINT(*-member-init)
|
||||
: m_invoke{asl::exchange(other.m_invoke, nullptr)}
|
||||
, m_op{asl::exchange(other.m_op, nullptr)}
|
||||
{
|
||||
if (m_op != nullptr)
|
||||
{
|
||||
m_op(
|
||||
&m_storage,
|
||||
&other.m_storage,
|
||||
function_detail::FunctionOp::kMoveFromOtherToThisUninit);
|
||||
}
|
||||
}
|
||||
|
||||
~function()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
function& operator=(const function& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
destroy();
|
||||
|
||||
m_invoke = other.m_invoke;
|
||||
m_op = other.m_op;
|
||||
|
||||
m_op(
|
||||
&m_storage,
|
||||
const_cast<function_detail::Storage*>(&other.m_storage), // NOLINT(*-const-cast)
|
||||
function_detail::FunctionOp::kCopyFromOtherToThisUninit);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
function& operator=(function&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
destroy();
|
||||
|
||||
m_invoke = asl::exchange(other.m_invoke, nullptr);
|
||||
m_op = asl::exchange(other.m_op, nullptr);
|
||||
|
||||
m_op(
|
||||
&m_storage,
|
||||
&other.m_storage,
|
||||
function_detail::FunctionOp::kMoveFromOtherToThisUninit);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
function& operator=(T&& func)
|
||||
requires (
|
||||
!same_as<function, un_cvref_t<T>>
|
||||
&& function_detail::valid_functor<T, R, Args...>
|
||||
)
|
||||
{
|
||||
destroy();
|
||||
|
||||
using Functor = decay_t<T>;
|
||||
using Impl = function_detail::FunctionImpl<Functor, R, Args...>;
|
||||
|
||||
Impl::create(&m_storage, std::forward<T>(func));
|
||||
m_invoke = &Impl::invoke;
|
||||
m_op = &Impl::op;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr R operator()(Args... args) const
|
||||
{
|
||||
ASL_ASSERT(m_invoke);
|
||||
return m_invoke(args..., m_storage);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asl
|
72
asl/types/function_ref.hpp
Normal file
72
asl/types/function_ref.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "asl/base/utility.hpp"
|
||||
#include "asl/base/meta.hpp"
|
||||
#include "asl/base/functional.hpp"
|
||||
#include "asl/memory/memory.hpp"
|
||||
|
||||
namespace asl
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class function_ref;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
class function_ref<R(Args...)>
|
||||
{
|
||||
using InvokeFn = R (*)(Args..., void*);
|
||||
|
||||
void* m_obj;
|
||||
InvokeFn m_invoke;
|
||||
|
||||
template<typename T>
|
||||
static R invoke(Args... args, void* obj)
|
||||
{
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
return asl::invoke(*reinterpret_cast<T*>(obj), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
public:
|
||||
function_ref() = delete;
|
||||
|
||||
ASL_DEFAULT_COPY_MOVE(function_ref);
|
||||
~function_ref() = default;
|
||||
|
||||
template<typename T>
|
||||
function_ref(T&& t) // NOLINT(*-missing-std-forward, *explicit*)
|
||||
requires (
|
||||
!same_as<un_cvref_t<T>, function_ref>
|
||||
&& invocable<T, Args...>
|
||||
&& same_as<invoke_result_t<T, Args...>, R>
|
||||
)
|
||||
// NOLINTNEXTLINE(*cast*)
|
||||
: m_obj{const_cast<void*>(reinterpret_cast<const void*>(address_of(t)))}
|
||||
, m_invoke{invoke<un_ref_t<T>>}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
function_ref& operator=(T&& t) // NOLINT(*-missing-std-forward)
|
||||
requires (
|
||||
!same_as<un_cvref_t<T>, function_ref>
|
||||
&& invocable<T, Args...>
|
||||
&& same_as<invoke_result_t<T, Args...>, R>
|
||||
)
|
||||
{
|
||||
// NOLINTNEXTLINE(*cast*)
|
||||
m_obj = const_cast<void*>(reinterpret_cast<const void*>(address_of(t)));
|
||||
m_invoke = invoke<un_ref_t<T>>;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr R operator()(this function_ref self, Args... args)
|
||||
{
|
||||
return self.m_invoke(std::forward<Args>(args)..., self.m_obj);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asl
|
54
asl/types/function_ref_tests.cpp
Normal file
54
asl/types/function_ref_tests.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "asl/testing/testing.hpp"
|
||||
#include "asl/types/function_ref.hpp"
|
||||
|
||||
static int add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
struct Functor
|
||||
{
|
||||
int state = 0;
|
||||
|
||||
int operator()(int x, int)
|
||||
{
|
||||
state += x;
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
static int invoke_fn_ref(asl::function_ref<int(int, int)> fn, int a, int b)
|
||||
{
|
||||
return fn(a, b);
|
||||
}
|
||||
|
||||
ASL_TEST(function_ref)
|
||||
{
|
||||
const asl::function_ref<int(int, int)> fn(add);
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fn, 4, 5) == 9);
|
||||
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(add, 4, 5) == 9);
|
||||
|
||||
ASL_TEST_EXPECT(invoke_fn_ref([](int a, int b) { return a * b; }, 4, 5) == 20);
|
||||
|
||||
Functor fun;
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fun, 4, 5) == 4);
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fun, 4, 5) == 8);
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fun, 4, 5) == 12);
|
||||
|
||||
asl::function_ref<int(int, int)> fn2 = fn;
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fn2, 4, 5) == 9);
|
||||
|
||||
fn2 = [](int a, int b) { return a - b; };
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fn2, 4, 5) == -1);
|
||||
|
||||
fn2 = fn;
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fn2, 4, 5) == 9);
|
||||
|
||||
fn2 = add;
|
||||
ASL_TEST_EXPECT(invoke_fn_ref(fn2, 4, 5) == 9);
|
||||
}
|
195
asl/types/function_tests.cpp
Normal file
195
asl/types/function_tests.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
// Copyright 2025 Steven Le Rouzic
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "asl/testing/testing.hpp"
|
||||
#include "asl/types/function.hpp"
|
||||
|
||||
static_assert(asl::function_detail::can_be_stored_inline<int(*)(int, int, int)>);
|
||||
static_assert(asl::function_detail::can_be_stored_inline<decltype([](){})>);
|
||||
static_assert(asl::function_detail::can_be_stored_inline<decltype([]() static {})>);
|
||||
static_assert(asl::function_detail::can_be_stored_inline<decltype([a = 1ULL, b = 2ULL](){ return a + b; })>); // NOLINT
|
||||
static_assert(asl::function_detail::can_be_stored_inline<decltype([a = 1ULL, b = 2ULL]() mutable { return a = b++; })>); // NOLINT
|
||||
static_assert(!asl::function_detail::can_be_stored_inline<decltype([a = 1ULL, b = 2ULL, c = 3ULL](){ return a + b + c; })>); // NOLINT
|
||||
|
||||
static int add(int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
|
||||
ASL_TEST(function_pointer)
|
||||
{
|
||||
const asl::function<int(int, int)> fn = add;
|
||||
ASL_TEST_EXPECT(fn(4, 5) == 9);
|
||||
ASL_TEST_EXPECT(fn(40, 50) == 90);
|
||||
}
|
||||
|
||||
ASL_TEST(lambda)
|
||||
{
|
||||
const asl::function<int(int, int)> fn = [](int a, int b) {
|
||||
return a + b;
|
||||
};
|
||||
ASL_TEST_EXPECT(fn(4, 5) == 9);
|
||||
}
|
||||
|
||||
ASL_TEST(lambda_static)
|
||||
{
|
||||
const asl::function<int(int, int)> fn = [](int a, int b) static {
|
||||
return a + b;
|
||||
};
|
||||
ASL_TEST_EXPECT(fn(4, 5) == 9);
|
||||
}
|
||||
|
||||
ASL_TEST(lambda_static_state)
|
||||
{
|
||||
const asl::function<int(int)> fn = [state = 0](int b) mutable {
|
||||
state += b;
|
||||
return state;
|
||||
};
|
||||
|
||||
ASL_TEST_EXPECT(fn(1) == 1);
|
||||
ASL_TEST_EXPECT(fn(2) == 3);
|
||||
ASL_TEST_EXPECT(fn(3) == 6);
|
||||
ASL_TEST_EXPECT(fn(4) == 10);
|
||||
}
|
||||
|
||||
ASL_TEST(lambda_state)
|
||||
{
|
||||
int state = 0;
|
||||
const asl::function<void(int)> fn = [&state](int x) {
|
||||
state += x;
|
||||
};
|
||||
|
||||
ASL_TEST_EXPECT(state == 0);
|
||||
|
||||
fn(5);
|
||||
ASL_TEST_EXPECT(state == 5);
|
||||
|
||||
fn(4);
|
||||
ASL_TEST_EXPECT(state == 9);
|
||||
}
|
||||
|
||||
ASL_TEST(lambda_big_state)
|
||||
{
|
||||
int s0 = 0;
|
||||
int s1 = 0;
|
||||
int s2 = 0;
|
||||
int s3 = 0;
|
||||
|
||||
const asl::function<void(int)> fn = [&](int x) {
|
||||
s0 += x;
|
||||
s1 += x + 1;
|
||||
s2 += x + 2;
|
||||
s3 += x + 3;
|
||||
};
|
||||
|
||||
ASL_TEST_EXPECT(s0 == 0);
|
||||
ASL_TEST_EXPECT(s1 == 0);
|
||||
ASL_TEST_EXPECT(s2 == 0);
|
||||
ASL_TEST_EXPECT(s3 == 0);
|
||||
|
||||
fn(5);
|
||||
ASL_TEST_EXPECT(s0 == 5);
|
||||
ASL_TEST_EXPECT(s1 == 6);
|
||||
ASL_TEST_EXPECT(s2 == 7);
|
||||
ASL_TEST_EXPECT(s3 == 8);
|
||||
|
||||
fn(4);
|
||||
ASL_TEST_EXPECT(s0 == 9);
|
||||
ASL_TEST_EXPECT(s1 == 11);
|
||||
ASL_TEST_EXPECT(s2 == 13);
|
||||
ASL_TEST_EXPECT(s3 == 15);
|
||||
}
|
||||
|
||||
struct Functor
|
||||
{
|
||||
int state{};
|
||||
|
||||
int operator()(int x)
|
||||
{
|
||||
state += x;
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
ASL_TEST(functor)
|
||||
{
|
||||
const asl::function<int(int)> fn = Functor{};
|
||||
|
||||
ASL_TEST_EXPECT(fn(1) == 1);
|
||||
ASL_TEST_EXPECT(fn(2) == 3);
|
||||
ASL_TEST_EXPECT(fn(3) == 6);
|
||||
ASL_TEST_EXPECT(fn(4) == 10);
|
||||
}
|
||||
|
||||
ASL_TEST(copy_move_construct_small)
|
||||
{
|
||||
asl::function<int(int, int)> fn = [x = 0](int a, int b) mutable { return x++ + a + b; };
|
||||
ASL_TEST_EXPECT(fn(1, 3) == 4);
|
||||
|
||||
asl::function<int(int, int)> fn2 = fn;
|
||||
ASL_TEST_EXPECT(fn(1, 3) == 5);
|
||||
ASL_TEST_EXPECT(fn2(5, 3) == 9);
|
||||
|
||||
asl::function<int(int, int)> fn3 = std::move(fn2);
|
||||
ASL_TEST_EXPECT(fn(1, 3) == 6);
|
||||
ASL_TEST_EXPECT(fn3(5, 3) == 10);
|
||||
|
||||
fn2 = fn;
|
||||
ASL_TEST_EXPECT(fn(1, 3) == 7);
|
||||
ASL_TEST_EXPECT(fn2(5, 3) == 11);
|
||||
ASL_TEST_EXPECT(fn3(5, 3) == 11);
|
||||
|
||||
fn3 = std::move(fn);
|
||||
ASL_TEST_EXPECT(fn2(5, 3) == 12);
|
||||
ASL_TEST_EXPECT(fn3(5, 3) == 12);
|
||||
}
|
||||
|
||||
ASL_TEST(copy_move_construct_big)
|
||||
{
|
||||
const int64_t v1 = 1;
|
||||
const int64_t v2 = 2;
|
||||
const int64_t v3 = 3;
|
||||
const int64_t v4 = 4;
|
||||
|
||||
asl::function<int64_t(int)> fn = [=](int x) { return v1 + v2 + v3 + v4 + x; };
|
||||
ASL_TEST_EXPECT(fn(1) == 11);
|
||||
|
||||
asl::function<int64_t(int)> fn2 = fn;
|
||||
ASL_TEST_EXPECT(fn(3) == 13);
|
||||
ASL_TEST_EXPECT(fn2(5) == 15);
|
||||
|
||||
asl::function<int64_t(int)> fn3 = std::move(fn2);
|
||||
ASL_TEST_EXPECT(fn(1) == 11);
|
||||
ASL_TEST_EXPECT(fn3(3) == 13);
|
||||
|
||||
fn2 = fn;
|
||||
ASL_TEST_EXPECT(fn(1) == 11);
|
||||
ASL_TEST_EXPECT(fn2(5) == 15);
|
||||
ASL_TEST_EXPECT(fn3(3) == 13);
|
||||
|
||||
fn3 = std::move(fn);
|
||||
ASL_TEST_EXPECT(fn2(5) == 15);
|
||||
ASL_TEST_EXPECT(fn3(3) == 13);
|
||||
}
|
||||
|
||||
ASL_TEST(replace)
|
||||
{
|
||||
asl::function<int(int)> fn = [](int x) { return x + 1; };
|
||||
ASL_TEST_EXPECT(fn(5) == 6);
|
||||
|
||||
fn = [](int x) { return x + 3; };
|
||||
ASL_TEST_EXPECT(fn(5) == 8);
|
||||
}
|
||||
|
||||
static int foo(const asl::function<int(int, int)>& fn)
|
||||
{
|
||||
return fn(5, 5);
|
||||
}
|
||||
|
||||
ASL_TEST(function_parameter)
|
||||
{
|
||||
ASL_TEST_EXPECT(foo(add) == 10);
|
||||
ASL_TEST_EXPECT(foo([](int a, int b) { return a + b; }) == 10);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
|
||||
explicit constexpr maybe_uninit(in_place_t, auto&&... args)
|
||||
requires constructible_from<T, decltype(args)...>
|
||||
: m_value{ASL_FWD(args)...}
|
||||
: m_value{std::forward<decltype(args)>(args)...}
|
||||
{}
|
||||
|
||||
constexpr maybe_uninit(const maybe_uninit&) requires trivially_copy_constructible<T> = default;
|
||||
@ -33,10 +33,10 @@ public:
|
||||
constexpr maybe_uninit(maybe_uninit&&) requires (!trivially_move_constructible<T>) {} // NOLINT
|
||||
|
||||
constexpr maybe_uninit& operator=(const maybe_uninit&) requires trivially_copy_assignable<T> = default;
|
||||
constexpr maybe_uninit& operator=(const maybe_uninit&) requires (!trivially_copy_assignable<T>) {}
|
||||
constexpr maybe_uninit& operator=(const maybe_uninit&) requires (!trivially_copy_assignable<T>) { return *this; } // NOLINT
|
||||
|
||||
constexpr maybe_uninit& operator=(maybe_uninit&&) requires trivially_move_assignable<T> = default;
|
||||
constexpr maybe_uninit& operator=(maybe_uninit&&) requires (!trivially_move_assignable<T>) {}
|
||||
constexpr maybe_uninit& operator=(maybe_uninit&&) requires (!trivially_move_assignable<T>) { return *this; } // NOLINT
|
||||
|
||||
constexpr ~maybe_uninit() requires trivially_destructible<T> = default;
|
||||
constexpr ~maybe_uninit() requires (!trivially_destructible<T>) {} // NOLINT
|
||||
@ -45,14 +45,14 @@ public:
|
||||
constexpr void construct_unsafe(auto&&... args)
|
||||
requires constructible_from<T, decltype(args)...>
|
||||
{
|
||||
construct_at<T>(&m_value, ASL_FWD(args)...);
|
||||
construct_at<T>(&m_value, std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
|
||||
// @Safety Value must have been initialized
|
||||
constexpr void assign_unsafe(auto&& value)
|
||||
requires assignable_from<T&, decltype(value)>
|
||||
{
|
||||
m_value = ASL_FWD(value);
|
||||
m_value = std::forward<decltype(value)>(value);
|
||||
}
|
||||
|
||||
// @Safety Value must have been initialized
|
||||
@ -67,7 +67,7 @@ public:
|
||||
// @Safety Value must have been initialized
|
||||
constexpr auto&& as_init_unsafe(this auto&& self)
|
||||
{
|
||||
return ASL_FWD(self).m_value;
|
||||
return std::forward<decltype(self)>(self).m_value;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,19 +74,19 @@ class option
|
||||
|
||||
if constexpr (!kHasNiche)
|
||||
{
|
||||
m_payload.construct_unsafe(ASL_FWD(args)...);
|
||||
m_payload.construct_unsafe(std::forward<Args>(args)...);
|
||||
m_has_value = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (move_assignable<T>)
|
||||
{
|
||||
m_payload.assign_unsafe(ASL_MOVE(T{ASL_FWD(args)...}));
|
||||
m_payload.assign_unsafe(T{std::forward<Args>(args)...});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_payload.destroy_unsafe();
|
||||
m_payload.construct_unsafe(ASL_FWD(args)...);
|
||||
m_payload.construct_unsafe(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,7 +95,7 @@ class option
|
||||
constexpr void assign(U&& arg)
|
||||
{
|
||||
ASL_ASSERT(has_value());
|
||||
m_payload.assign_unsafe(ASL_FWD(arg));
|
||||
m_payload.assign_unsafe(std::forward<U>(arg));
|
||||
}
|
||||
|
||||
public:
|
||||
@ -103,32 +103,32 @@ public:
|
||||
|
||||
constexpr option() : option{nullopt} {}
|
||||
|
||||
// NOLINTNEXTLINE(*-explicit-conversions)
|
||||
// NOLINTNEXTLINE(*explicit*)
|
||||
constexpr option(nullopt_t) requires (!kHasNiche) {}
|
||||
|
||||
// NOLINTNEXTLINE(*-explicit-conversions)
|
||||
// NOLINTNEXTLINE(*explicit*)
|
||||
constexpr option(nullopt_t) requires kHasNiche : m_payload{in_place, niche_t{}} {}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr explicit (!convertible_from<T, U&&>)
|
||||
constexpr explicit (!convertible_to<U&&, T>)
|
||||
option(U&& value)
|
||||
requires (
|
||||
kHasNiche &&
|
||||
constructible_from<T, U&&> &&
|
||||
!same_as<un_cvref_t<U>, option>
|
||||
)
|
||||
: m_payload{in_place, ASL_FWD(value)}
|
||||
: m_payload{in_place, std::forward<U>(value)}
|
||||
{}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr explicit (!convertible_from<T, U&&>)
|
||||
constexpr explicit (!convertible_to<U&&, T>)
|
||||
option(U&& value)
|
||||
requires (
|
||||
!kHasNiche &&
|
||||
constructible_from<T, U&&> &&
|
||||
!is_option<U>
|
||||
)
|
||||
: m_payload{in_place, ASL_FWD(value)}
|
||||
: m_payload{in_place, std::forward<U>(value)}
|
||||
, m_has_value{true}
|
||||
{}
|
||||
|
||||
@ -154,12 +154,12 @@ public:
|
||||
{
|
||||
if (other.has_value())
|
||||
{
|
||||
construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
|
||||
construct(std::move(other.m_payload.as_init_unsafe()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr explicit (!convertible_from<T, const U&>)
|
||||
constexpr explicit (!convertible_to<const U&, T>)
|
||||
option(const option<U>& other)
|
||||
requires (
|
||||
constructible_from<T, const U&> &&
|
||||
@ -174,7 +174,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr explicit (!convertible_from<T, U&&>)
|
||||
constexpr explicit (!convertible_to<U&&, T>)
|
||||
option(option<U>&& other)
|
||||
requires (
|
||||
constructible_from<T, U&&> &&
|
||||
@ -184,7 +184,7 @@ public:
|
||||
{
|
||||
if (other.has_value())
|
||||
{
|
||||
construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
|
||||
construct(std::move(other).m_payload.as_init_unsafe());
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,11 +204,11 @@ public:
|
||||
{
|
||||
if (has_value())
|
||||
{
|
||||
assign(ASL_FWD(value));
|
||||
assign(std::forward<U>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
construct(ASL_FWD(value));
|
||||
construct(std::forward<U>(value));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@ -259,11 +259,11 @@ public:
|
||||
{
|
||||
if (has_value())
|
||||
{
|
||||
assign(ASL_MOVE(other.m_payload.as_init_unsafe()));
|
||||
assign(std::move(other.m_payload.as_init_unsafe()));
|
||||
}
|
||||
else
|
||||
{
|
||||
construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
|
||||
construct(std::move(other.m_payload.as_init_unsafe()));
|
||||
}
|
||||
}
|
||||
else if (has_value())
|
||||
@ -313,11 +313,11 @@ public:
|
||||
{
|
||||
if (has_value())
|
||||
{
|
||||
assign(ASL_MOVE(other.m_payload.as_init_unsafe()));
|
||||
assign(std::move(other).m_payload.as_init_unsafe());
|
||||
}
|
||||
else
|
||||
{
|
||||
construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
|
||||
construct(std::move(other).m_payload.as_init_unsafe());
|
||||
}
|
||||
}
|
||||
else if (has_value())
|
||||
@ -342,7 +342,7 @@ public:
|
||||
{
|
||||
if constexpr (move_assignable<T>)
|
||||
{
|
||||
m_payload.assign_unsafe(ASL_MOVE(T{niche_t{}}));
|
||||
m_payload.assign_unsafe(std::move(T{niche_t{}}));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -357,7 +357,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool has_value() const
|
||||
[[nodiscard]] constexpr bool has_value() const
|
||||
{
|
||||
if constexpr (kHasNiche)
|
||||
{
|
||||
@ -372,28 +372,28 @@ public:
|
||||
constexpr auto&& value(this auto&& self)
|
||||
{
|
||||
ASL_ASSERT_RELEASE(self.has_value());
|
||||
return ASL_FWD(self).m_payload.as_init_unsafe();
|
||||
return std::forward<decltype(self)>(self).m_payload.as_init_unsafe();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr T value_or(U&& other_value) const&
|
||||
requires copy_constructible<T> && convertible_from<T, U&&>
|
||||
requires copy_constructible<T> && convertible_to<U&&, T>
|
||||
{
|
||||
return has_value() ? value() : static_cast<T>(ASL_FWD(other_value));
|
||||
return has_value() ? value() : static_cast<T>(std::forward<U>(other_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr T value_or(U&& other_value) &&
|
||||
requires move_constructible<T> && convertible_from<T, U&&>
|
||||
requires move_constructible<T> && convertible_to<U&&, T>
|
||||
{
|
||||
return has_value() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value));
|
||||
return has_value() ? std::move(value()) : static_cast<T>(std::forward<U>(other_value));
|
||||
}
|
||||
|
||||
constexpr T& emplace(auto&&... args) &
|
||||
requires constructible_from<T, decltype(args)...>
|
||||
{
|
||||
if (has_value()) { reset(); }
|
||||
construct(ASL_FWD(args)...);
|
||||
construct(std::forward<decltype(args)>(args)...);
|
||||
return value();
|
||||
}
|
||||
|
||||
@ -405,7 +405,7 @@ public:
|
||||
|
||||
if (self.has_value())
|
||||
{
|
||||
return invoke(ASL_FWD(f), ASL_FWD(self).value());
|
||||
return invoke(std::forward<F>(f), std::forward<decltype(self)>(self).value());
|
||||
}
|
||||
return Result{ asl::nullopt };
|
||||
}
|
||||
@ -417,7 +417,7 @@ public:
|
||||
if (self.has_value())
|
||||
{
|
||||
return option<un_cvref_t<Result>>{
|
||||
invoke(ASL_FWD(f), ASL_FWD(self).value())
|
||||
invoke(std::forward<F>(f), std::forward<decltype(self)>(self).value())
|
||||
};
|
||||
}
|
||||
return option<un_cvref_t<Result>>{ asl::nullopt };
|
||||
@ -427,14 +427,14 @@ public:
|
||||
constexpr option or_else(F&& f) const&
|
||||
requires same_as<un_cvref_t<invoke_result_t<F>>, option>
|
||||
{
|
||||
return has_value() ? *this : invoke(ASL_FWD(f));
|
||||
return has_value() ? *this : invoke(std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr option or_else(F&& f) &&
|
||||
requires same_as<un_cvref_t<invoke_result_t<F>>, option>
|
||||
{
|
||||
return has_value() ? ASL_MOVE(*this) : invoke(ASL_FWD(f));
|
||||
return has_value() ? std::move(*this) : invoke(std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename H>
|
||||
@ -443,9 +443,9 @@ public:
|
||||
{
|
||||
if (!opt.has_value())
|
||||
{
|
||||
return H::combine(ASL_MOVE(h), 0);
|
||||
return H::combine(std::move(h), 0);
|
||||
}
|
||||
return H::combine(ASL_MOVE(h), 1, opt.value());
|
||||
return H::combine(std::move(h), 1, opt.value());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -62,20 +62,20 @@ static_assert(!asl::move_assignable<asl::option<Pinned>>);
|
||||
static_assert(asl::assignable_from<asl::option<Base*>&, asl::option<Derived*>>);
|
||||
static_assert(!asl::assignable_from<asl::option<Derived*>&, asl::option<Base*>>);
|
||||
|
||||
static_assert(asl::convertible_from<asl::option<Base*>, asl::option<Derived*>>);
|
||||
static_assert(!asl::convertible_from<asl::option<Derived*>, asl::option<Base*>>);
|
||||
static_assert(asl::convertible_to<asl::option<Derived*>, asl::option<Base*>>);
|
||||
static_assert(!asl::convertible_to<asl::option<Base*>, asl::option<Derived*>>);
|
||||
|
||||
class ExplicitConversion { public: explicit ExplicitConversion(int) {} };
|
||||
class ImplicitConversion { public: ImplicitConversion(int) {} }; // NOLINT
|
||||
|
||||
static_assert(!asl::convertible_from<ExplicitConversion, int>);
|
||||
static_assert(asl::convertible_from<ImplicitConversion, int>);
|
||||
static_assert(!asl::convertible_to<int, ExplicitConversion>);
|
||||
static_assert(asl::convertible_to<int, ImplicitConversion>);
|
||||
|
||||
static_assert(!asl::convertible_from<asl::option<ExplicitConversion>, int>);
|
||||
static_assert(asl::convertible_from<asl::option<ImplicitConversion>, int>);
|
||||
static_assert(!asl::convertible_to<int, asl::option<ExplicitConversion>>);
|
||||
static_assert(asl::convertible_to<int, asl::option<ImplicitConversion>>);
|
||||
|
||||
static_assert(!asl::convertible_from<asl::option<ExplicitConversion>, asl::option<int>>);
|
||||
static_assert(asl::convertible_from<asl::option<ImplicitConversion>, asl::option<int>>);
|
||||
static_assert(!asl::convertible_to<asl::option<int>, asl::option<ExplicitConversion>>);
|
||||
static_assert(asl::convertible_to<asl::option<int>, asl::option<ImplicitConversion>>);
|
||||
|
||||
static_assert(asl::trivially_copy_constructible<asl::option<int>>);
|
||||
static_assert(asl::trivially_copy_constructible<asl::option<TrivialType>>);
|
||||
@ -99,8 +99,8 @@ static_assert(!asl::trivially_move_assignable<asl::option<MoveableOnly>>);
|
||||
|
||||
ASL_TEST(make_null)
|
||||
{
|
||||
asl::option<int> a;
|
||||
asl::option<int> b = asl::nullopt;
|
||||
const asl::option<int> a;
|
||||
const asl::option<int> b = asl::nullopt;
|
||||
|
||||
ASL_TEST_EXPECT(!a.has_value());
|
||||
ASL_TEST_EXPECT(!b.has_value());
|
||||
@ -108,7 +108,7 @@ ASL_TEST(make_null)
|
||||
|
||||
ASL_TEST(make_value)
|
||||
{
|
||||
asl::option<int> a = 48;
|
||||
const asl::option<int> a = 48;
|
||||
|
||||
ASL_TEST_EXPECT(a.has_value());
|
||||
}
|
||||
@ -129,10 +129,10 @@ ASL_TEST(call_destructor)
|
||||
{
|
||||
DestructorObserver obs(&destroyed);
|
||||
|
||||
asl::option<DestructorObserver> opt(ASL_MOVE(obs));
|
||||
asl::option<DestructorObserver> opt(std::move(obs));
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
|
||||
asl::option<DestructorObserver> opt2 = ASL_MOVE(opt);
|
||||
const asl::option<DestructorObserver> opt2 = std::move(opt);
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ ASL_TEST(value_move)
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
|
||||
{
|
||||
auto x = ASL_MOVE(opt).value();
|
||||
auto x = std::move(opt).value();
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
}
|
||||
|
||||
@ -208,27 +208,27 @@ ASL_TEST(convert_copy)
|
||||
ASL_TEST(convert_move)
|
||||
{
|
||||
asl::option<uint8_t> opt8 = uint8_t{8};
|
||||
asl::option<uint16_t> opt16 = ASL_MOVE(opt8);
|
||||
asl::option<uint16_t> opt16 = std::move(opt8);
|
||||
|
||||
ASL_TEST_ASSERT(opt16.has_value());
|
||||
ASL_TEST_EXPECT(opt16.value() == 8);
|
||||
|
||||
opt8 = ASL_MOVE(uint8_t{10});
|
||||
opt8 = uint8_t{10};
|
||||
ASL_TEST_ASSERT(opt8.has_value());
|
||||
ASL_TEST_EXPECT(opt8.value() == 10);
|
||||
|
||||
opt16 = asl::nullopt;
|
||||
ASL_TEST_EXPECT(!opt16.has_value());
|
||||
|
||||
opt16 = ASL_MOVE(opt8);
|
||||
opt16 = std::move(opt8);
|
||||
ASL_TEST_ASSERT(opt16.has_value());
|
||||
ASL_TEST_EXPECT(opt16.value() == 10);
|
||||
}
|
||||
|
||||
ASL_TEST(value_or)
|
||||
{
|
||||
asl::option<int> a = asl::nullopt;
|
||||
asl::option<int> b = 2;
|
||||
const asl::option<int> a = asl::nullopt;
|
||||
const asl::option<int> b = 2;
|
||||
|
||||
ASL_TEST_EXPECT(a.value_or(5) == 5);
|
||||
ASL_TEST_EXPECT(b.value_or(5) == 2);
|
||||
@ -296,8 +296,8 @@ ASL_TEST(transform)
|
||||
|
||||
ASL_TEST(or_else)
|
||||
{
|
||||
asl::option<int> a = 5;
|
||||
asl::option<int> b;
|
||||
const asl::option<int> a = 5;
|
||||
const asl::option<int> b;
|
||||
|
||||
auto fn = []() -> asl::option<int> { return 12; };
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace asl
|
||||
|
||||
static constexpr isize_t dynamic_size = -1;
|
||||
|
||||
// NOLINTBEGIN(*-convert-member-functions-to-static)
|
||||
template<typename T>
|
||||
class contiguous_iterator
|
||||
{
|
||||
@ -22,7 +23,7 @@ class contiguous_iterator
|
||||
public:
|
||||
constexpr explicit contiguous_iterator(T* ptr) : m_ptr{ptr} {}
|
||||
|
||||
constexpr bool operator==(const contiguous_iterator& other) const = default;
|
||||
constexpr bool operator==(this contiguous_iterator self, contiguous_iterator other) = default;
|
||||
|
||||
constexpr contiguous_iterator& operator++()
|
||||
{
|
||||
@ -35,9 +36,9 @@ public:
|
||||
return contiguous_iterator{ exchange(m_ptr, m_ptr + 1) };
|
||||
}
|
||||
|
||||
constexpr T& operator*() const { return *m_ptr; }
|
||||
constexpr T& operator*(this contiguous_iterator self) { return *self.m_ptr; }
|
||||
|
||||
constexpr T* operator->() const { return m_ptr; }
|
||||
constexpr T* operator->(this contiguous_iterator self) { return self.m_ptr; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -75,27 +76,27 @@ public:
|
||||
}
|
||||
|
||||
template<isize_t N>
|
||||
constexpr span(T (&array)[N]) // NOLINT(*-explicit-conversions)
|
||||
constexpr span(T (&array)[N]) // NOLINT(*explicit*)
|
||||
requires (kIsDynamic)
|
||||
: m_data{array}
|
||||
: m_data{static_cast<T*>(array)}
|
||||
, m_size{N}
|
||||
{}
|
||||
|
||||
template<isize_t N>
|
||||
constexpr span(T (&array)[N]) // NOLINT(*-explicit-conversions)
|
||||
constexpr span(T (&array)[N]) // NOLINT(*explicit*)
|
||||
requires (!kIsDynamic) && (N == kSize)
|
||||
: m_data{array}
|
||||
: m_data{static_cast<T*>(array)}
|
||||
{}
|
||||
|
||||
template<is_object U, isize_t kOtherSize>
|
||||
constexpr explicit(!kIsDynamic)
|
||||
span(const span<U, kOtherSize>& other) // NOLINT(*-explicit-conversions)
|
||||
span(const span<U, kOtherSize>& other)
|
||||
requires (
|
||||
(
|
||||
kIsDynamic ||
|
||||
is_dynamic(kOtherSize) ||
|
||||
kOtherSize == kSize
|
||||
) && convertible_from<T(&)[], U(&)[]>
|
||||
) && convertible_to<U(&)[], T(&)[]>
|
||||
)
|
||||
: span{static_cast<U*>(other.data()), other.size()}
|
||||
{
|
||||
@ -109,115 +110,116 @@ public:
|
||||
|
||||
~span() = default;
|
||||
|
||||
constexpr isize_t size() const
|
||||
[[nodiscard]] constexpr isize_t size(this span self)
|
||||
{
|
||||
if constexpr (kIsDynamic) { return m_size; }
|
||||
if constexpr (kIsDynamic) { return self.m_size; }
|
||||
else { return kSize; }
|
||||
}
|
||||
|
||||
constexpr isize_t size_bytes() const { return size() * size_of<T>; }
|
||||
[[nodiscard]] constexpr isize_t size_bytes(this span self) { return self.size() * size_of<T>; }
|
||||
|
||||
constexpr bool is_empty() const { return size() == 0; }
|
||||
[[nodiscard]] constexpr bool is_empty(this span self) { return self.size() == 0; }
|
||||
|
||||
constexpr T* data() const { return m_data; }
|
||||
[[nodiscard]] constexpr T* data(this span self) { return self.m_data; }
|
||||
|
||||
constexpr contiguous_iterator<T> begin() const
|
||||
[[nodiscard]] constexpr contiguous_iterator<T> begin(this span self)
|
||||
{
|
||||
return contiguous_iterator{m_data};
|
||||
return contiguous_iterator{self.m_data};
|
||||
}
|
||||
|
||||
constexpr contiguous_iterator<T> end() const
|
||||
[[nodiscard]] constexpr contiguous_iterator<T> end(this span self)
|
||||
{
|
||||
return contiguous_iterator{m_data + size()};
|
||||
return contiguous_iterator{self.m_data + self.size()};
|
||||
}
|
||||
|
||||
constexpr T& operator[](isize_t i) const
|
||||
constexpr T& operator[](this span self, isize_t i)
|
||||
{
|
||||
ASL_ASSERT(i >= 0 && i < size());
|
||||
return m_data[i]; // NOLINT(*-pointer-arithmetic)
|
||||
ASL_ASSERT(i >= 0 && i < self.size());
|
||||
return self.m_data[i]; // NOLINT(*-pointer-arithmetic)
|
||||
}
|
||||
|
||||
template<isize_t kOffset, isize_t kSubSize = dynamic_size>
|
||||
constexpr auto subspan() const
|
||||
[[nodiscard]] constexpr auto subspan(this span self)
|
||||
requires (
|
||||
kOffset >= 0 &&
|
||||
(kIsDynamic || kOffset <= kSize) &&
|
||||
(kIsDynamic || is_dynamic(kSubSize) || kSubSize <= kSize - kOffset)
|
||||
)
|
||||
{
|
||||
ASL_ASSERT(kOffset <= size());
|
||||
ASL_ASSERT(kOffset <= self.size());
|
||||
|
||||
if constexpr (is_dynamic(kSubSize))
|
||||
{
|
||||
if constexpr (kIsDynamic)
|
||||
{
|
||||
return span<T>(data() + kOffset, size() - kOffset);
|
||||
return span<T>(self.data() + kOffset, self.size() - kOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return span<T, kSize - kOffset>(data() + kOffset, size() - kOffset);
|
||||
return span<T, kSize - kOffset>(self.data() + kOffset, self.size() - kOffset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASL_ASSERT(kSubSize <= size() - kOffset);
|
||||
return span<T, kSubSize>(data() + kOffset, kSubSize);
|
||||
ASL_ASSERT(kSubSize <= self.size() - kOffset);
|
||||
return span<T, kSubSize>(self.data() + kOffset, kSubSize);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr span<T> subspan(isize_t offset) const
|
||||
[[nodiscard]] constexpr span<T> subspan(this span self, isize_t offset)
|
||||
{
|
||||
ASL_ASSERT(offset <= size());
|
||||
return span<T>{ data() + offset, size() - offset };
|
||||
ASL_ASSERT(offset <= self.size());
|
||||
return span<T>{ self.data() + offset, self.size() - offset };
|
||||
}
|
||||
|
||||
constexpr span<T> subspan(isize_t offset, isize_t sub_size) const
|
||||
[[nodiscard]] constexpr span<T> subspan(this span self, isize_t offset, isize_t sub_size)
|
||||
{
|
||||
ASL_ASSERT(offset <= size() && !is_dynamic(sub_size));
|
||||
ASL_ASSERT(sub_size <= size() - offset);
|
||||
return span<T>{ data() + offset, sub_size };
|
||||
ASL_ASSERT(offset <= self.size() && !is_dynamic(sub_size));
|
||||
ASL_ASSERT(sub_size <= self.size() - offset);
|
||||
return span<T>{ self.data() + offset, sub_size };
|
||||
}
|
||||
|
||||
template<isize_t kSubSize>
|
||||
constexpr auto first() const
|
||||
[[nodiscard]] constexpr auto first(this span self)
|
||||
requires (
|
||||
kSubSize >= 0 &&
|
||||
(kIsDynamic || kSubSize <= kSize)
|
||||
)
|
||||
{
|
||||
ASL_ASSERT(kSubSize <= size());
|
||||
return span<T, kSubSize>{ data(), kSubSize };
|
||||
ASL_ASSERT(kSubSize <= self.size());
|
||||
return span<T, kSubSize>{ self.data(), kSubSize };
|
||||
}
|
||||
|
||||
constexpr span<T> first(isize_t sub_size) const
|
||||
[[nodiscard]] constexpr span<T> first(this span self, isize_t sub_size)
|
||||
{
|
||||
ASL_ASSERT(sub_size >= 0 && sub_size <= size());
|
||||
return span<T>{ data(), sub_size };
|
||||
ASL_ASSERT(sub_size >= 0 && sub_size <= self.size());
|
||||
return span<T>{ self.data(), sub_size };
|
||||
}
|
||||
|
||||
template<isize_t kSubSize>
|
||||
constexpr auto last() const
|
||||
[[nodiscard]] constexpr auto last(this span self)
|
||||
requires (
|
||||
kSubSize >= 0 &&
|
||||
(kIsDynamic || kSubSize <= kSize)
|
||||
)
|
||||
{
|
||||
ASL_ASSERT(kSubSize <= size());
|
||||
return span<T, kSubSize>{ data() + size() - kSubSize, kSubSize };
|
||||
ASL_ASSERT(kSubSize <= self.size());
|
||||
return span<T, kSubSize>{ self.data() + self.size() - kSubSize, kSubSize };
|
||||
}
|
||||
|
||||
constexpr span<T> last(isize_t sub_size) const
|
||||
[[nodiscard]] constexpr span<T> last(this span self, isize_t sub_size)
|
||||
{
|
||||
ASL_ASSERT(sub_size >= 0 && sub_size <= size());
|
||||
return span<T>{ data() + size() - sub_size, sub_size };
|
||||
ASL_ASSERT(sub_size >= 0 && sub_size <= self.size());
|
||||
return span<T>{ self.data() + self.size() - sub_size, sub_size };
|
||||
}
|
||||
};
|
||||
// NOLINTEND(*-convert-member-functions-to-static)
|
||||
|
||||
template<is_object T, isize_t kSize>
|
||||
inline span<const byte> as_bytes(span<T, kSize> s)
|
||||
{
|
||||
return span<const byte>(
|
||||
reinterpret_cast<const byte*>(s.data()),
|
||||
reinterpret_cast<const byte*>(s.data()), // NOLINT(*-reinterpret-cast)
|
||||
s.size_bytes());
|
||||
}
|
||||
|
||||
@ -226,7 +228,7 @@ inline span<byte> as_mutable_bytes(span<T, kSize> s)
|
||||
requires (!is_const<T>)
|
||||
{
|
||||
return span<byte>(
|
||||
reinterpret_cast<byte*>(s.data()),
|
||||
reinterpret_cast<byte*>(s.data()), // NOLINT(*-reinterpret-cast)
|
||||
s.size_bytes());
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ static_assert(asl::size_of<asl::span<int, 2>> == asl::size_of<void*>);
|
||||
|
||||
ASL_TEST(empty_dynamic)
|
||||
{
|
||||
asl::span<int> s;
|
||||
const asl::span<int> s;
|
||||
ASL_TEST_EXPECT(s.size() == 0);
|
||||
ASL_TEST_EXPECT(s.size_bytes() == 0);
|
||||
ASL_TEST_EXPECT(s.is_empty());
|
||||
@ -26,7 +26,7 @@ ASL_TEST(empty_dynamic)
|
||||
|
||||
ASL_TEST(empty_static)
|
||||
{
|
||||
asl::span<int, 0> s;
|
||||
const asl::span<int, 0> s;
|
||||
ASL_TEST_EXPECT(s.size() == 0);
|
||||
ASL_TEST_EXPECT(s.size_bytes() == 0);
|
||||
ASL_TEST_EXPECT(s.is_empty());
|
||||
@ -35,7 +35,7 @@ ASL_TEST(empty_static)
|
||||
ASL_TEST(from_array_dynamic)
|
||||
{
|
||||
int array[] = {1, 2, 3};
|
||||
asl::span<int> span = array;
|
||||
const asl::span<int> span = array;
|
||||
ASL_TEST_ASSERT(span.size() == 3);
|
||||
ASL_TEST_EXPECT(span[0] == 1);
|
||||
ASL_TEST_EXPECT(span[1] == 2);
|
||||
@ -56,7 +56,7 @@ static_assert(!asl::constructible_from<asl::span<int32_t, 8>, int32_t(&)[10]>);
|
||||
ASL_TEST(from_array_static)
|
||||
{
|
||||
int array[] = {1, 2, 3};
|
||||
asl::span<int, 3> span = array;
|
||||
const asl::span<int, 3> span = array;
|
||||
ASL_TEST_ASSERT(span.size() == 3);
|
||||
ASL_TEST_EXPECT(span[0] == 1);
|
||||
ASL_TEST_EXPECT(span[1] == 2);
|
||||
@ -77,21 +77,21 @@ ASL_TEST(conversion)
|
||||
{
|
||||
int array[] = {1, 2, 3};
|
||||
|
||||
asl::span<int> span1 = array;
|
||||
const asl::span<int> span1 = array;
|
||||
|
||||
asl::span<int, 3> span2{span1};
|
||||
const asl::span<int, 3> span2{span1};
|
||||
ASL_TEST_ASSERT(span2.size() == 3);
|
||||
ASL_TEST_EXPECT(span2[0] == 1);
|
||||
ASL_TEST_EXPECT(span2[1] == 2);
|
||||
ASL_TEST_EXPECT(span2[2] == 3);
|
||||
|
||||
asl::span<int> span3 = span2;
|
||||
const asl::span<int> span3 = span2;
|
||||
ASL_TEST_ASSERT(span3.size() == 3);
|
||||
ASL_TEST_EXPECT(span3[0] == 1);
|
||||
ASL_TEST_EXPECT(span3[1] == 2);
|
||||
ASL_TEST_EXPECT(span3[2] == 3);
|
||||
|
||||
asl::span<const int, 3> span4{span2};
|
||||
const asl::span<const int, 3> span4{span2};
|
||||
ASL_TEST_ASSERT(span4.size() == 3);
|
||||
ASL_TEST_EXPECT(span4[0] == 1);
|
||||
ASL_TEST_EXPECT(span4[1] == 2);
|
||||
@ -138,7 +138,7 @@ static_assert(!IsValidSubspan<asl::span<int, 4>, 2, 3>);
|
||||
ASL_TEST(subspan_static_from_static)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int, 4> span{array};
|
||||
const asl::span<int, 4> span{array};
|
||||
|
||||
auto s1 = span.subspan<0>();
|
||||
ASL_TEST_ASSERT(s1.size() == 4);
|
||||
@ -164,7 +164,7 @@ ASL_TEST(subspan_static_from_static)
|
||||
ASL_TEST(subspan_static_from_dynamic)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int> span{array};
|
||||
const asl::span<int> span{array};
|
||||
|
||||
auto s1 = span.subspan<0>();
|
||||
ASL_TEST_ASSERT(s1.size() == 4);
|
||||
@ -190,7 +190,7 @@ ASL_TEST(subspan_static_from_dynamic)
|
||||
ASL_TEST(subspan_dynamic)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int> span{array};
|
||||
const asl::span<int> span{array};
|
||||
|
||||
auto s1 = span.subspan(0);
|
||||
ASL_TEST_ASSERT(s1.size() == 4);
|
||||
@ -249,7 +249,7 @@ static_assert(!IsValidFirst<asl::span<int, 4>, asl::dynamic_size>);
|
||||
ASL_TEST(first_static_from_static)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int, 4> span{array};
|
||||
const asl::span<int, 4> span{array};
|
||||
|
||||
auto s1 = span.first<0>();
|
||||
ASL_TEST_ASSERT(s1.size() == 0);
|
||||
@ -270,7 +270,7 @@ ASL_TEST(first_static_from_static)
|
||||
ASL_TEST(first_static_from_dynamic)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int> span{array};
|
||||
const asl::span<int> span{array};
|
||||
|
||||
auto s1 = span.first<0>();
|
||||
ASL_TEST_ASSERT(s1.size() == 0);
|
||||
@ -291,7 +291,7 @@ ASL_TEST(first_static_from_dynamic)
|
||||
ASL_TEST(first_dynamic)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int> span{array};
|
||||
const asl::span<int> span{array};
|
||||
|
||||
auto s1 = span.first(0);
|
||||
ASL_TEST_ASSERT(s1.size() == 0);
|
||||
@ -345,7 +345,7 @@ static_assert(!IsValidLast<asl::span<int, 4>, asl::dynamic_size>);
|
||||
ASL_TEST(last_static_from_static)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int, 4> span{array};
|
||||
const asl::span<int, 4> span{array};
|
||||
|
||||
auto s1 = span.last<0>();
|
||||
ASL_TEST_ASSERT(s1.size() == 0);
|
||||
@ -366,7 +366,7 @@ ASL_TEST(last_static_from_static)
|
||||
ASL_TEST(last_static_from_dynamic)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int> span{array};
|
||||
const asl::span<int> span{array};
|
||||
|
||||
auto s1 = span.last<0>();
|
||||
ASL_TEST_ASSERT(s1.size() == 0);
|
||||
@ -387,7 +387,7 @@ ASL_TEST(last_static_from_dynamic)
|
||||
ASL_TEST(last_dynamic)
|
||||
{
|
||||
int array[] = {1, 2, 3, 4};
|
||||
asl::span<int> span{array};
|
||||
const asl::span<int> span{array};
|
||||
|
||||
auto s1 = span.last(0);
|
||||
ASL_TEST_ASSERT(s1.size() == 0);
|
||||
@ -416,8 +416,8 @@ static_assert(HasAsMutableBytes<const int*>);
|
||||
ASL_TEST(as_bytes)
|
||||
{
|
||||
uint32_t data[] = {0x01020304, 0x05060708};
|
||||
asl::span s1(data);
|
||||
asl::span s2 = asl::as_bytes(s1);
|
||||
const asl::span s1(data);
|
||||
const asl::span s2 = asl::as_bytes(s1);
|
||||
|
||||
ASL_TEST_ASSERT(s2.size() == 8);
|
||||
ASL_TEST_ASSERT(static_cast<int>(s2[0]) == 0x04);
|
||||
@ -433,8 +433,8 @@ ASL_TEST(as_bytes)
|
||||
ASL_TEST(as_mutable_bytes)
|
||||
{
|
||||
uint32_t data[] = {0x01020304, 0x05060708};
|
||||
asl::span s1(data);
|
||||
asl::span s2 = asl::as_mutable_bytes(s1);
|
||||
const asl::span s1(data);
|
||||
const asl::span s2 = asl::as_mutable_bytes(s1);
|
||||
|
||||
ASL_TEST_ASSERT(s2.size() == 8);
|
||||
ASL_TEST_ASSERT(static_cast<int>(s2[0]) == 0x04);
|
||||
|
@ -11,9 +11,11 @@
|
||||
|
||||
// @Todo Use custom allocator
|
||||
using Allocator = asl::DefaultAllocator;
|
||||
|
||||
// NOLINTNEXTLINE(*-non-const-global-variables)
|
||||
static Allocator g_allocator{};
|
||||
|
||||
namespace
|
||||
namespace asl
|
||||
{
|
||||
|
||||
struct StatusInternal
|
||||
@ -23,7 +25,7 @@ struct StatusInternal
|
||||
asl::atomic<int32_t> ref_count;
|
||||
|
||||
constexpr StatusInternal(asl::string<Allocator>&& msg_, asl::status_code code_)
|
||||
: msg{ASL_MOVE(msg_)}
|
||||
: msg{std::move(msg_)}
|
||||
, code{code_}
|
||||
{
|
||||
ASL_ASSERT(code != asl::status_code::ok);
|
||||
@ -31,7 +33,7 @@ struct StatusInternal
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
} // namespace asl
|
||||
|
||||
asl::status::status(status_code code, string_view msg)
|
||||
: m_payload{alloc_new<StatusInternal>(g_allocator, msg, code)}
|
||||
@ -41,36 +43,35 @@ asl::status::status(status_code code, string_view fmt, span<format_internals::ty
|
||||
{
|
||||
StringWriter<Allocator> sink{g_allocator};
|
||||
format_internals::format(&sink, fmt, args);
|
||||
m_payload = alloc_new<StatusInternal>(g_allocator, ASL_MOVE(sink).finish(), code);
|
||||
m_payload = alloc_new<StatusInternal>(g_allocator, std::move(sink).finish(), code);
|
||||
}
|
||||
|
||||
asl::status_code asl::status::code_internal() const
|
||||
{
|
||||
ASL_ASSERT(!is_inline());
|
||||
return reinterpret_cast<const StatusInternal*>(m_payload)->code;
|
||||
return m_payload->code;
|
||||
}
|
||||
|
||||
asl::string_view asl::status::message_internal() const
|
||||
{
|
||||
ASL_ASSERT(!is_inline());
|
||||
return reinterpret_cast<const StatusInternal*>(m_payload)->msg;
|
||||
return m_payload->msg;
|
||||
}
|
||||
|
||||
void asl::status::ref()
|
||||
{
|
||||
ASL_ASSERT(!is_inline());
|
||||
auto* internal = reinterpret_cast<StatusInternal*>(m_payload);
|
||||
atomic_fetch_increment(&internal->ref_count, memory_order::relaxed);
|
||||
atomic_fetch_increment(&m_payload->ref_count, memory_order::relaxed);
|
||||
}
|
||||
|
||||
void asl::status::unref()
|
||||
{
|
||||
ASL_ASSERT(!is_inline());
|
||||
auto* internal = reinterpret_cast<StatusInternal*>(m_payload);
|
||||
if (atomic_fetch_decrement(&internal->ref_count, memory_order::release) == 1)
|
||||
if (atomic_fetch_decrement(&m_payload->ref_count, memory_order::release) == 1)
|
||||
{
|
||||
atomic_fence(memory_order::acquire);
|
||||
alloc_delete(g_allocator, internal);
|
||||
alloc_delete(g_allocator, m_payload);
|
||||
m_payload = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,15 +22,17 @@ enum class status_code : uint8_t
|
||||
invalid_argument = 4,
|
||||
};
|
||||
|
||||
struct StatusInternal;
|
||||
|
||||
class status
|
||||
{
|
||||
void* m_payload{};
|
||||
StatusInternal* m_payload{};
|
||||
|
||||
static constexpr void* status_to_payload(status_code code)
|
||||
static constexpr StatusInternal* status_to_payload(status_code code)
|
||||
{
|
||||
return code == status_code::ok
|
||||
? nullptr
|
||||
: bit_cast<void*>((static_cast<uintptr_t>(code) << 1) | 1);
|
||||
: bit_cast<StatusInternal*>((static_cast<uintptr_t>(code) << 1) | 1);
|
||||
}
|
||||
|
||||
static constexpr status_code payload_to_status(void* payload)
|
||||
@ -38,12 +40,12 @@ class status
|
||||
return static_cast<status_code>(bit_cast<uintptr_t>(payload) >> 1);
|
||||
}
|
||||
|
||||
constexpr bool is_inline() const
|
||||
[[nodiscard]] constexpr bool is_inline() const
|
||||
{
|
||||
return m_payload == nullptr || (bit_cast<uintptr_t>(m_payload) & 1) != 0;
|
||||
}
|
||||
|
||||
constexpr status_code code_inline() const
|
||||
[[nodiscard]] constexpr status_code code_inline() const
|
||||
{
|
||||
ASL_ASSERT(is_inline());
|
||||
if (m_payload == nullptr)
|
||||
@ -53,8 +55,8 @@ class status
|
||||
return payload_to_status(m_payload);
|
||||
}
|
||||
|
||||
status_code code_internal() const;
|
||||
string_view message_internal() const;
|
||||
[[nodiscard]] status_code code_internal() const;
|
||||
[[nodiscard]] string_view message_internal() const;
|
||||
|
||||
void ref();
|
||||
void unref();
|
||||
@ -103,17 +105,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr bool ok() const
|
||||
[[nodiscard]] constexpr bool ok() const
|
||||
{
|
||||
return m_payload == nullptr;
|
||||
}
|
||||
|
||||
constexpr status_code code() const
|
||||
[[nodiscard]] constexpr status_code code() const
|
||||
{
|
||||
return is_inline() ? code_inline() : code_internal();
|
||||
}
|
||||
|
||||
constexpr string_view message() const
|
||||
[[nodiscard]] constexpr string_view message() const
|
||||
{
|
||||
if (!is_inline())
|
||||
{
|
||||
@ -122,7 +124,7 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr status&& throw_status() && { return ASL_MOVE(*this); }
|
||||
constexpr status&& throw_status() && { return std::move(*this); }
|
||||
|
||||
friend void AslFormat(Formatter& f, const status&);
|
||||
|
||||
@ -131,9 +133,9 @@ public:
|
||||
{
|
||||
if (s.is_inline())
|
||||
{
|
||||
return H::combine(ASL_MOVE(h), s.code());
|
||||
return H::combine(std::move(h), s.code());
|
||||
}
|
||||
return H::combine(ASL_MOVE(h), s.code(), s.message());
|
||||
return H::combine(std::move(h), s.code(), s.message());
|
||||
}
|
||||
};
|
||||
|
||||
@ -156,6 +158,6 @@ ASL_DEFINE_ERROR_(internal)
|
||||
ASL_DEFINE_ERROR_(runtime)
|
||||
ASL_DEFINE_ERROR_(invalid_argument)
|
||||
|
||||
#define ASL_TRY(VALUE) if (VALUE.ok()) {} else { return ASL_MOVE(VALUE).throw_status(); }
|
||||
#define ASL_TRY(VALUE) if ((VALUE).ok()) {} else { return std::move(VALUE).throw_status(); }
|
||||
|
||||
} // namespace asl
|
||||
|
@ -33,11 +33,11 @@ public:
|
||||
|
||||
constexpr status_or(status_or&& other)
|
||||
requires move_constructible<T>
|
||||
: m_status{ASL_MOVE(other.m_status)}
|
||||
: m_status{std::move(other.m_status)}
|
||||
{
|
||||
if (other.ok())
|
||||
{
|
||||
m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
|
||||
m_value.construct_unsafe(std::move(other.m_value.as_init_unsafe()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ public:
|
||||
{
|
||||
if (other.ok())
|
||||
{
|
||||
m_value.assign_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
|
||||
m_value.assign_unsafe(std::move(other.m_value.as_init_unsafe()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -84,9 +84,9 @@ public:
|
||||
}
|
||||
else if (other.ok())
|
||||
{
|
||||
m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
|
||||
m_value.construct_unsafe(std::move(other.m_value.as_init_unsafe()));
|
||||
}
|
||||
m_status = ASL_MOVE(other.m_status);
|
||||
m_status = std::move(other.m_status);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -99,14 +99,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-explicit-conversions)
|
||||
// NOLINTNEXTLINE(*explicit*)
|
||||
constexpr status_or(const status& status) : m_status{status}
|
||||
{
|
||||
ASL_ASSERT_RELEASE(!m_status.ok());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-explicit-conversions)
|
||||
constexpr status_or(status&& status) : m_status{ASL_MOVE(status)}
|
||||
// NOLINTNEXTLINE(*explicit*)
|
||||
constexpr status_or(status&& status) : m_status{std::move(status)}
|
||||
{
|
||||
ASL_ASSERT_RELEASE(!m_status.ok());
|
||||
}
|
||||
@ -114,7 +114,7 @@ public:
|
||||
status_or& operator=(status status) = delete;
|
||||
|
||||
template<typename U = T>
|
||||
constexpr explicit (!convertible_from<T, U&&>)
|
||||
constexpr explicit (!convertible_to<U&&, T>)
|
||||
status_or(U&& value)
|
||||
requires (
|
||||
constructible_from<T, U&&> &&
|
||||
@ -122,35 +122,35 @@ public:
|
||||
!same_as<un_cvref_t<U>, status>
|
||||
)
|
||||
: m_status{status_code::ok}
|
||||
, m_value{in_place, ASL_FWD(value)}
|
||||
, m_value{in_place, std::forward<U>(value)}
|
||||
{}
|
||||
|
||||
constexpr bool ok() const { return m_status.ok(); }
|
||||
[[nodiscard]] constexpr bool ok() const { return m_status.ok(); }
|
||||
|
||||
constexpr status_code code() const { return m_status.code(); }
|
||||
[[nodiscard]] constexpr status_code code() const { return m_status.code(); }
|
||||
|
||||
constexpr string_view message() const { return m_status.message(); }
|
||||
[[nodiscard]] constexpr string_view message() const { return m_status.message(); }
|
||||
|
||||
constexpr status&& throw_status() && { return ASL_MOVE(m_status); }
|
||||
constexpr status&& throw_status() && { return std::move(m_status); }
|
||||
|
||||
constexpr auto&& value(this auto&& self)
|
||||
{
|
||||
ASL_ASSERT_RELEASE(self.ok());
|
||||
return ASL_FWD(self).m_value.as_init_unsafe();
|
||||
return std::forward<decltype(self)>(self).m_value.as_init_unsafe();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr T value_or(U&& other_value) const&
|
||||
requires copy_constructible<T> && convertible_from<T, U&&>
|
||||
requires copy_constructible<T> && convertible_to<U&&, T>
|
||||
{
|
||||
return ok() ? value() : static_cast<T>(ASL_FWD(other_value));
|
||||
return ok() ? value() : static_cast<T>(std::forward<U>(other_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr T value_or(U&& other_value) &&
|
||||
requires move_constructible<T> && convertible_from<T, U&&>
|
||||
requires move_constructible<T> && convertible_to<U&&, T>
|
||||
{
|
||||
return ok() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value));
|
||||
return ok() ? std::move(value()) : static_cast<T>(std::forward<U>(other_value));
|
||||
}
|
||||
|
||||
friend void AslFormat(Formatter& f, const status_or& status)
|
||||
@ -164,9 +164,9 @@ public:
|
||||
{
|
||||
if (s.ok())
|
||||
{
|
||||
return H::combine(ASL_MOVE(h), s.m_status, s.value());
|
||||
return H::combine(std::move(h), s.m_status, s.value());
|
||||
}
|
||||
return H::combine(ASL_MOVE(h), s.m_status);
|
||||
return H::combine(std::move(h), s.m_status);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -20,24 +20,24 @@ static_assert(!asl::moveable<asl::status_or<Pinned>>);
|
||||
|
||||
ASL_TEST(ok)
|
||||
{
|
||||
asl::status_or<int> s = 6;
|
||||
const asl::status_or<int> s = 6;
|
||||
ASL_TEST_EXPECT(s.ok());
|
||||
ASL_TEST_EXPECT(s.code() == asl::status_code::ok);
|
||||
}
|
||||
|
||||
ASL_TEST(from_status)
|
||||
{
|
||||
asl::status_or<char> s = asl::internal_error();
|
||||
const asl::status_or<char> s = asl::internal_error();
|
||||
ASL_TEST_EXPECT(!s.ok());
|
||||
ASL_TEST_EXPECT(s.code() == asl::status_code::internal);
|
||||
ASL_TEST_EXPECT(s.message() == ""_sv);
|
||||
|
||||
asl::status_or<int> s2 = asl::internal_error("oh no");
|
||||
const asl::status_or<int> s2 = asl::internal_error("oh no");
|
||||
ASL_TEST_EXPECT(!s2.ok());
|
||||
ASL_TEST_EXPECT(s2.code() == asl::status_code::internal);
|
||||
ASL_TEST_EXPECT(s2.message() == "oh no"_sv);
|
||||
|
||||
asl::status_or<int> s3 = asl::internal_error("{} {}", 1, 2);
|
||||
const asl::status_or<int> s3 = asl::internal_error("{} {}", 1, 2);
|
||||
ASL_TEST_EXPECT(!s3.ok());
|
||||
ASL_TEST_EXPECT(s3.code() == asl::status_code::internal);
|
||||
ASL_TEST_EXPECT(s3.message() == "1 2"_sv);
|
||||
@ -52,11 +52,11 @@ ASL_TEST(destructor)
|
||||
ASL_TEST_EXPECT(s.ok());
|
||||
ASL_TEST_EXPECT(!d);
|
||||
|
||||
asl::status_or s2 = ASL_MOVE(s);
|
||||
ASL_TEST_EXPECT(s.ok());
|
||||
asl::status_or s2 = std::move(s);
|
||||
ASL_TEST_EXPECT(s2.ok());
|
||||
ASL_TEST_EXPECT(!d);
|
||||
|
||||
s = ASL_MOVE(s2);
|
||||
s = std::move(s2);
|
||||
ASL_TEST_EXPECT(s.ok());
|
||||
ASL_TEST_EXPECT(!d);
|
||||
}
|
||||
@ -79,8 +79,8 @@ ASL_TEST(copy)
|
||||
|
||||
ASL_TEST(value_or)
|
||||
{
|
||||
asl::status_or<int> s = 7;
|
||||
asl::status_or<int> s2 = asl::internal_error();
|
||||
const asl::status_or<int> s = 7;
|
||||
const asl::status_or<int> s2 = asl::internal_error();
|
||||
|
||||
ASL_TEST_EXPECT(s.value_or(45) == 7);
|
||||
ASL_TEST_EXPECT(s2.value_or(45) == 45);
|
||||
|
@ -9,14 +9,14 @@
|
||||
|
||||
ASL_TEST(simple_ok)
|
||||
{
|
||||
asl::status s = asl::ok();
|
||||
const asl::status s = asl::ok();
|
||||
ASL_TEST_ASSERT(s.ok());
|
||||
ASL_TEST_ASSERT(s.code() == asl::status_code::ok);
|
||||
}
|
||||
|
||||
ASL_TEST(simple_code)
|
||||
{
|
||||
asl::status s = asl::runtime_error();
|
||||
const asl::status s = asl::runtime_error();
|
||||
ASL_TEST_ASSERT(!s.ok());
|
||||
ASL_TEST_ASSERT(s.code() == asl::status_code::runtime);
|
||||
ASL_TEST_ASSERT(s.message() == ""_sv);
|
||||
@ -24,7 +24,7 @@ ASL_TEST(simple_code)
|
||||
|
||||
ASL_TEST(with_message)
|
||||
{
|
||||
asl::status s = asl::internal_error("We done goofed");
|
||||
const asl::status s = asl::internal_error("We done goofed");
|
||||
ASL_TEST_ASSERT(!s.ok());
|
||||
ASL_TEST_ASSERT(s.code() == asl::status_code::internal);
|
||||
ASL_TEST_ASSERT(s.message() == "We done goofed"_sv);
|
||||
@ -32,8 +32,8 @@ ASL_TEST(with_message)
|
||||
|
||||
ASL_TEST(copy_inline)
|
||||
{
|
||||
asl::status s = asl::ok();
|
||||
asl::status s2 = asl::internal_error();
|
||||
const asl::status s = asl::ok();
|
||||
const asl::status s2 = asl::internal_error();
|
||||
|
||||
asl::status s3 = s;
|
||||
ASL_TEST_ASSERT(s3.code() == asl::status_code::ok);
|
||||
@ -47,7 +47,7 @@ ASL_TEST(copy_message)
|
||||
asl::status s2 = asl::ok();
|
||||
|
||||
{
|
||||
asl::status s = asl::internal_error("Oh no!");
|
||||
const asl::status s = asl::internal_error("Oh no!");
|
||||
ASL_TEST_ASSERT(!s.ok());
|
||||
ASL_TEST_ASSERT(s.code() == asl::status_code::internal);
|
||||
ASL_TEST_ASSERT(s.message() == "Oh no!"_sv);
|
||||
|
@ -12,6 +12,7 @@ import time
|
||||
TO_FIX = [
|
||||
".bazelrc",
|
||||
".clang-tidy",
|
||||
".clangd",
|
||||
".gitignore",
|
||||
"**/*.hpp",
|
||||
"**/*.h",
|
||||
|
Reference in New Issue
Block a user