From c1f95fed9cf7f4190ba2ef672ecafff42ac46407 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Wed, 25 Feb 2026 17:21:39 +0500 Subject: [PATCH 01/11] add c++20 --- aether/CMakeLists.txt | 2 +- .../benches/send_message_delays/CMakeLists.txt | 3 +++ .../benches/send_messages_bandwidth/CMakeLists.txt | 2 +- .../send_messages_bandwidth/common/CMakeLists.txt | 3 +++ .../receiver/CMakeLists.txt | 3 +++ .../send_messages_bandwidth/sender/CMakeLists.txt | 4 ++++ examples/cloud/CMakeLists.txt | 3 +++ projects/cmake/CMakeLists.txt | 14 ++++---------- .../platformio/aether-client-cpp/CMakeLists.txt | 3 --- .../vscode/aether-client-cpp/CMakeLists.txt | 3 --- .../platformio/aether-client-cpp/CMakeLists.txt | 3 --- .../vscode/aether-client-cpp/CMakeLists.txt | 3 --- tests/CMakeLists.txt | 6 ++++++ tests/benchmarking.h | 3 +-- tests/test-domain-storage/CMakeLists.txt | 2 +- tests/test-events/CMakeLists.txt | 6 ++---- tests/test-object-system/CMakeLists.txt | 4 ++-- tests/test-ptr/CMakeLists.txt | 6 ++---- tests/test-ptr/test-rc-ptr-bench.cpp | 6 +++--- tests/test-ptr/test-shared-ptr-bench.cpp | 6 +++--- tests/test-tele/CMakeLists.txt | 5 +++-- tools/registrator/CMakeLists.txt | 2 +- 22 files changed, 46 insertions(+), 46 deletions(-) diff --git a/aether/CMakeLists.txt b/aether/CMakeLists.txt index 60adb1a1..269f8b08 100644 --- a/aether/CMakeLists.txt +++ b/aether/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.16.0) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(AE_PROJECT_VERSION "0.1.0") diff --git a/examples/benches/send_message_delays/CMakeLists.txt b/examples/benches/send_message_delays/CMakeLists.txt index 4afb4506..8223ed39 100644 --- a/examples/benches/send_message_delays/CMakeLists.txt +++ b/examples/benches/send_message_delays/CMakeLists.txt @@ -14,6 +14,9 @@ cmake_minimum_required(VERSION 3.16.0) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + list( APPEND api_srcs api/bench_delays_api.cpp ) diff --git a/examples/benches/send_messages_bandwidth/CMakeLists.txt b/examples/benches/send_messages_bandwidth/CMakeLists.txt index 229fff60..c77888f1 100644 --- a/examples/benches/send_messages_bandwidth/CMakeLists.txt +++ b/examples/benches/send_messages_bandwidth/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.16) if(NOT CM_PLATFORM) - add_subdirectory("common") + add_subdirectory("common") add_subdirectory("sender") add_subdirectory("receiver") else() diff --git a/examples/benches/send_messages_bandwidth/common/CMakeLists.txt b/examples/benches/send_messages_bandwidth/common/CMakeLists.txt index 7954a7e2..2b201f5d 100644 --- a/examples/benches/send_messages_bandwidth/common/CMakeLists.txt +++ b/examples/benches/send_messages_bandwidth/common/CMakeLists.txt @@ -14,6 +14,9 @@ cmake_minimum_required(VERSION 3.16) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + list( APPEND src_list bandwidth_api.cpp bandwidth.cpp diff --git a/examples/benches/send_messages_bandwidth/receiver/CMakeLists.txt b/examples/benches/send_messages_bandwidth/receiver/CMakeLists.txt index e0cdeb9d..ecd42d84 100644 --- a/examples/benches/send_messages_bandwidth/receiver/CMakeLists.txt +++ b/examples/benches/send_messages_bandwidth/receiver/CMakeLists.txt @@ -14,6 +14,9 @@ cmake_minimum_required(VERSION 3.16.0) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + list( APPEND src_list receiver_main.cpp receiver.cpp diff --git a/examples/benches/send_messages_bandwidth/sender/CMakeLists.txt b/examples/benches/send_messages_bandwidth/sender/CMakeLists.txt index f8f7131a..8673cad2 100644 --- a/examples/benches/send_messages_bandwidth/sender/CMakeLists.txt +++ b/examples/benches/send_messages_bandwidth/sender/CMakeLists.txt @@ -14,6 +14,10 @@ cmake_minimum_required(VERSION 3.16.0) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + list( APPEND src_list sender_main.cpp sender.cpp diff --git a/examples/cloud/CMakeLists.txt b/examples/cloud/CMakeLists.txt index d00bbab1..db6d974e 100644 --- a/examples/cloud/CMakeLists.txt +++ b/examples/cloud/CMakeLists.txt @@ -14,6 +14,9 @@ cmake_minimum_required(VERSION 3.16.0) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + list( APPEND src_list main.cpp cloud_test.cpp diff --git a/projects/cmake/CMakeLists.txt b/projects/cmake/CMakeLists.txt index cc27e9de..71bc8f50 100644 --- a/projects/cmake/CMakeLists.txt +++ b/projects/cmake/CMakeLists.txt @@ -19,14 +19,8 @@ cmake_minimum_required(VERSION 3.16) project(Aether LANGUAGES C CXX) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - option(AE_ADDRESS_SANITIZE Off "Enable address sanitizer") -# enable doubles in unity tests -add_compile_definitions("UNITY_INCLUDE_DOUBLE") - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) if(NOT DEFINED AE_FILTRATION) @@ -39,9 +33,11 @@ if (AE_ADDRESS_SANITIZE) add_link_options(-fsanitize=address) endif() -# for unit tests -enable_testing() +if (AE_BUILD_TESTS) + enable_testing() +endif() +add_subdirectory("../../aether" "aether") add_subdirectory("../../examples/cloud" "cloud") add_subdirectory("../../examples/capi/oddity" "oddity") @@ -49,5 +45,3 @@ add_subdirectory("../../examples/benches/send_message_delays" "send_message_dela add_subdirectory("../../examples/benches/send_messages_bandwidth" "send_messages_bandwidth") add_subdirectory("../../tools/registrator" "registrator") - -add_subdirectory("../../tests" "tests") diff --git a/projects/espressif_riscv/platformio/aether-client-cpp/CMakeLists.txt b/projects/espressif_riscv/platformio/aether-client-cpp/CMakeLists.txt index 508c4c43..b09da698 100644 --- a/projects/espressif_riscv/platformio/aether-client-cpp/CMakeLists.txt +++ b/projects/espressif_riscv/platformio/aether-client-cpp/CMakeLists.txt @@ -18,9 +18,6 @@ cmake_minimum_required(VERSION 3.16.0) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - idf_build_set_property(CM_PLATFORM "ESP32") add_compile_definitions(CM_ESP32) diff --git a/projects/espressif_riscv/vscode/aether-client-cpp/CMakeLists.txt b/projects/espressif_riscv/vscode/aether-client-cpp/CMakeLists.txt index 4ab52606..4fc68fdc 100644 --- a/projects/espressif_riscv/vscode/aether-client-cpp/CMakeLists.txt +++ b/projects/espressif_riscv/vscode/aether-client-cpp/CMakeLists.txt @@ -18,9 +18,6 @@ cmake_minimum_required(VERSION 3.16.0) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - idf_build_set_property(CM_PLATFORM "ESP32") add_compile_definitions(CM_ESP32) diff --git a/projects/xtensa_lx6/platformio/aether-client-cpp/CMakeLists.txt b/projects/xtensa_lx6/platformio/aether-client-cpp/CMakeLists.txt index 508c4c43..b09da698 100644 --- a/projects/xtensa_lx6/platformio/aether-client-cpp/CMakeLists.txt +++ b/projects/xtensa_lx6/platformio/aether-client-cpp/CMakeLists.txt @@ -18,9 +18,6 @@ cmake_minimum_required(VERSION 3.16.0) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - idf_build_set_property(CM_PLATFORM "ESP32") add_compile_definitions(CM_ESP32) diff --git a/projects/xtensa_lx6/vscode/aether-client-cpp/CMakeLists.txt b/projects/xtensa_lx6/vscode/aether-client-cpp/CMakeLists.txt index c310c074..f81e5a2b 100644 --- a/projects/xtensa_lx6/vscode/aether-client-cpp/CMakeLists.txt +++ b/projects/xtensa_lx6/vscode/aether-client-cpp/CMakeLists.txt @@ -18,9 +18,6 @@ cmake_minimum_required(VERSION 3.16.0) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - idf_build_set_property(CM_PLATFORM "ESP32") add_compile_definitions(CM_ESP32) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e9050b4a..d2f4c0a2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,9 +14,15 @@ cmake_minimum_required( VERSION 3.16 ) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/run) set(ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/..") +# enable doubles in unity tests +add_compile_definitions("UNITY_INCLUDE_DOUBLE") + #dependancies if (NOT TARGET aether) add_subdirectory("${ROOT_DIR}/aether" "aether") diff --git a/tests/benchmarking.h b/tests/benchmarking.h index dfd733b9..58922c32 100644 --- a/tests/benchmarking.h +++ b/tests/benchmarking.h @@ -21,14 +21,13 @@ #include #include #include -#include namespace ae::tests { template void BenchmarkFunc(Func&& func, std::size_t count, MessageArgs&&... message_args) { auto time_before = std::chrono::steady_clock::now(); - for (volatile std::size_t i = 0; i < count; ++i) { + for (std::size_t i = 0; i < count; ++i) { func(i); } auto time_after = std::chrono::steady_clock::now(); diff --git a/tests/test-domain-storage/CMakeLists.txt b/tests/test-domain-storage/CMakeLists.txt index 3c397b71..fdd214cb 100644 --- a/tests/test-domain-storage/CMakeLists.txt +++ b/tests/test-domain-storage/CMakeLists.txt @@ -22,7 +22,7 @@ if(NOT CM_PLATFORM) project(test-domain-storage LANGUAGES CXX) add_executable(${PROJECT_NAME}) - target_sources(${PROJECT_NAME} PRIVATE ${aether_srcs} ${test_srcs}) + target_sources(${PROJECT_NAME} PRIVATE ${test_srcs}) # for aether target_include_directories(${PROJECT_NAME} PRIVATE ${ROOT_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE aether unity) diff --git a/tests/test-events/CMakeLists.txt b/tests/test-events/CMakeLists.txt index a7833740..c794c548 100644 --- a/tests/test-events/CMakeLists.txt +++ b/tests/test-events/CMakeLists.txt @@ -14,14 +14,12 @@ cmake_minimum_required( VERSION 3.16 ) -list(APPEND events_srcs +list(APPEND test_events_srcs ${ROOT_DIR}/aether/events/event_list.cpp ${ROOT_DIR}/aether/events/event_deleter.cpp ${ROOT_DIR}/aether/events/event_subscription.cpp ${ROOT_DIR}/aether/events/multi_subscription.cpp -) -list(APPEND test_events_srcs main.cpp test-events.cpp test-events-mt.cpp @@ -31,7 +29,7 @@ if(NOT CM_PLATFORM) project(test-events LANGUAGES CXX) add_executable(${PROJECT_NAME}) - target_sources(${PROJECT_NAME} PRIVATE ${events_srcs} ${test_events_srcs}) + target_sources(${PROJECT_NAME} PRIVATE ${test_events_srcs}) target_include_directories(${PROJECT_NAME} PRIVATE ${ROOT_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE unity) diff --git a/tests/test-object-system/CMakeLists.txt b/tests/test-object-system/CMakeLists.txt index b9c5cc79..e3248da1 100644 --- a/tests/test-object-system/CMakeLists.txt +++ b/tests/test-object-system/CMakeLists.txt @@ -19,7 +19,7 @@ list(APPEND other_aether_srcs ${ROOT_DIR}/aether/tele/traps/statistics_trap.cpp ) -list(APPEND obj_srcs +list(APPEND test_obj_srcs ${ROOT_DIR}/aether/ptr/ptr.cpp ${ROOT_DIR}/aether/ptr/ptr_view.cpp ${ROOT_DIR}/aether/ptr/ref_tree.cpp @@ -46,7 +46,7 @@ if(NOT CM_PLATFORM) target_sources(${PROJECT_NAME} PRIVATE ${other_aether_srcs} - ${obj_srcs} + ${test_obj_srcs} ${test_srcs} ) # for aether diff --git a/tests/test-ptr/CMakeLists.txt b/tests/test-ptr/CMakeLists.txt index cd6d0009..cedc8396 100644 --- a/tests/test-ptr/CMakeLists.txt +++ b/tests/test-ptr/CMakeLists.txt @@ -16,13 +16,11 @@ cmake_minimum_required( VERSION 3.16 ) option(AE_RC_PTR_BENCH "Make a benchmark for rc ptr compared to std::shared_ptr" Off) -list(APPEND aether_srcs +list(APPEND test_srcs ${ROOT_DIR}/aether/ptr/ptr.cpp ${ROOT_DIR}/aether/ptr/ptr_view.cpp ${ROOT_DIR}/aether/ptr/ref_tree.cpp -) -list(APPEND test_srcs main.cpp test-rc-ptr.cpp test-rc-ptr-bench.cpp @@ -37,7 +35,7 @@ if(NOT CM_PLATFORM) project(test-ptr LANGUAGES CXX) add_executable(${PROJECT_NAME}) - target_sources(${PROJECT_NAME} PRIVATE ${aether_srcs} ${test_srcs}) + target_sources(${PROJECT_NAME} PRIVATE ${test_srcs}) # for aether target_include_directories(${PROJECT_NAME} PRIVATE ${ROOT_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE unity) diff --git a/tests/test-ptr/test-rc-ptr-bench.cpp b/tests/test-ptr/test-rc-ptr-bench.cpp index 5df1d1ce..53a46666 100644 --- a/tests/test-ptr/test-rc-ptr-bench.cpp +++ b/tests/test-ptr/test-rc-ptr-bench.cpp @@ -32,7 +32,7 @@ static constexpr std::size_t kBenchCount = 100'000'000; struct Rabbit { Rabbit(int y) { x = y * y; } ~Rabbit() { x = x - x / 2; } - volatile int x{0}; + int x{0}; }; void test_rc_ptr_CreationBench() { @@ -48,7 +48,7 @@ void test_rc_ptr_CreationBench() { rabbits1.reserve(1000); tests::BenchmarkFunc( [&](auto i) { - for (volatile auto j = 0; j < rabbits1.capacity(); j++) { + for (auto j = 0; j < rabbits1.capacity(); j++) { rabbits1.push_back(MakeRcPtr(static_cast(i + j))); rabbits1.back()->x += static_cast(i) % 100; } @@ -73,7 +73,7 @@ void test_rc_ptr_CopyingBench() { std::vector> rabbits3; rabbits3.reserve(1000); - for (volatile std::size_t j = 0; j < rabbits3.capacity(); j++) { + for (std::size_t j = 0; j < rabbits3.capacity(); j++) { rabbits3.push_back(MakeRcPtr(static_cast(j))); } tests::BenchmarkFunc( diff --git a/tests/test-ptr/test-shared-ptr-bench.cpp b/tests/test-ptr/test-shared-ptr-bench.cpp index 5c18819c..6bea0a87 100644 --- a/tests/test-ptr/test-shared-ptr-bench.cpp +++ b/tests/test-ptr/test-shared-ptr-bench.cpp @@ -31,7 +31,7 @@ static constexpr std::size_t kBenchCount = 100'000'000; struct Rabbit { explicit Rabbit(int y) : x{y * y} {} ~Rabbit() { x = x - (x / 2); } - volatile int x{0}; + int x{0}; }; void test_shared_ptr_CreationBench() { @@ -47,7 +47,7 @@ void test_shared_ptr_CreationBench() { rabbits1.reserve(1000); tests::BenchmarkFunc( [&](auto i) { - for (volatile auto j = 0; j < rabbits1.capacity(); j++) { + for (auto j = 0; j < rabbits1.capacity(); j++) { rabbits1.push_back(std::make_shared(i + j)); rabbits1.back()->x += static_cast(i) % 100; } @@ -73,7 +73,7 @@ void test_shared_ptr_CopyingBench() { std::vector> rabbits3; rabbits3.reserve(1000); - for (volatile std::size_t j = 0; j < rabbits3.capacity(); j++) { + for (std::size_t j = 0; j < rabbits3.capacity(); j++) { rabbits3.push_back(std::make_shared(j)); } tests::BenchmarkFunc( diff --git a/tests/test-tele/CMakeLists.txt b/tests/test-tele/CMakeLists.txt index 087e8d2c..6bf8ddcf 100644 --- a/tests/test-tele/CMakeLists.txt +++ b/tests/test-tele/CMakeLists.txt @@ -14,13 +14,14 @@ cmake_minimum_required( VERSION 3.16 ) -list(APPEND tele_srcs +list(APPEND test_tele_srcs ${ROOT_DIR}/aether/tele/traps/io_stream_traps.cpp ${ROOT_DIR}/aether/tele/traps/statistics_trap.cpp ${ROOT_DIR}/aether/ptr/ptr.cpp ${ROOT_DIR}/aether/ptr/ptr_view.cpp ${ROOT_DIR}/aether/ptr/ref_tree.cpp ) + list(APPEND test_srcs test-tele.cpp ) @@ -29,7 +30,7 @@ if(NOT CM_PLATFORM) project(test-tele LANGUAGES CXX) add_executable(${PROJECT_NAME}) - target_sources(${PROJECT_NAME} PRIVATE ${tele_srcs} ${test_srcs}) + target_sources(${PROJECT_NAME} PRIVATE ${test_tele_srcs} ${test_srcs}) # for aether target_include_directories(${PROJECT_NAME} PRIVATE ${ROOT_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE unity) diff --git a/tools/registrator/CMakeLists.txt b/tools/registrator/CMakeLists.txt index 3c9b059a..0982766a 100644 --- a/tools/registrator/CMakeLists.txt +++ b/tools/registrator/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.16.0) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT TARGET aether) From 1ec1df2fd91d99b28dde35031cf27f0d2ca7f791 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Wed, 25 Feb 2026 17:22:28 +0500 Subject: [PATCH 02/11] add inline tests --- aether/CMakeLists.txt | 12 +- tests/inline_tests/CMakeLists.txt | 22 +++ tests/inline_tests/inline_tests.cmake | 61 ++++++++ tests/inline_tests/tests/crc.h | 216 ++++++++++++++++++++++++++ tests/inline_tests/tests/inline.h | 52 +++++++ tests/inline_tests/tests/main.cpp.in | 40 +++++ 6 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 tests/inline_tests/CMakeLists.txt create mode 100644 tests/inline_tests/inline_tests.cmake create mode 100644 tests/inline_tests/tests/crc.h create mode 100644 tests/inline_tests/tests/inline.h create mode 100644 tests/inline_tests/tests/main.cpp.in diff --git a/aether/CMakeLists.txt b/aether/CMakeLists.txt index 269f8b08..baa251df 100644 --- a/aether/CMakeLists.txt +++ b/aether/CMakeLists.txt @@ -22,6 +22,7 @@ set(AE_PROJECT_VERSION "0.1.0") option(AE_NO_STRIP_ALL "Do not apply --strip_all, useful for bloaty and similar tools " Off) option(AE_DISTILLATION "Build aether in distillation mode" On) option(AE_FILTRATION "Build aether in filtration mode" Off) +option(AE_BUILD_TESTS "Build tests" Off) set(UTM_ID "0" CACHE STRING "User Tracking Measurement ID, must be a uint32 value") set(USER_CONFIG "" CACHE PATH "Path to user provided configuration header file") @@ -546,8 +547,17 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(${TARGET_NAME} PUBLIC /Zc:preprocessor) endif() -if(REGULAR_CMAKE_PROJECT) +if(AE_BUILD_TESTS) + message(STATUS "Aether builds tests!") + # enable doubles in unity tests + add_compile_definitions("UNITY_INCLUDE_DOUBLE") + + target_link_libraries(${TARGET_NAME} PRIVATE inline_tests) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../tests ${CMAKE_BINARY_DIR}/tests) +endif() + +if(REGULAR_CMAKE_PROJECT AND NOT AE_BUILD_TESTS) ## Target installation install(TARGETS ${TARGET_NAME} EXPORT ${TARGET_NAME}Targets diff --git a/tests/inline_tests/CMakeLists.txt b/tests/inline_tests/CMakeLists.txt new file mode 100644 index 00000000..0c6f4ccc --- /dev/null +++ b/tests/inline_tests/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2026 Aethernet Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.16) + +project(inline_tests) + +add_library(inline_tests INTERFACE) +target_include_directories(inline_tests INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(inline_tests INTERFACE unity) +target_compile_definitions(inline_tests INTERFACE "-DAE_TESTS=1") diff --git a/tests/inline_tests/inline_tests.cmake b/tests/inline_tests/inline_tests.cmake new file mode 100644 index 00000000..826573b4 --- /dev/null +++ b/tests/inline_tests/inline_tests.cmake @@ -0,0 +1,61 @@ +# Copyright 2026 Aethernet Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.16) + +set(inline_tests_dir "${CMAKE_CURRENT_LIST_DIR}") + +# Add a new inline test +# REPO_DIR and REPO_LIB must be visible in scope +function(add_inline_test file) + file(RELATIVE_PATH file_relative ${REPO_DIR} ${file}) + string(REGEX REPLACE "[\\. \/ \-]" "_" test_name "${file_relative}" ) + set(test_name "test_${test_name}") + + set(TEST_FILE ${file}) + configure_file("${inline_tests_dir}/tests/main.cpp.in" + "${CMAKE_CURRENT_BINARY_DIR}/generated/${test_name}.cpp" + @ONLY) + + message(STATUS "Add inline test [${test_name}] for ${file}") + + add_executable(${test_name} "${CMAKE_CURRENT_BINARY_DIR}/generated/${test_name}.cpp") + target_link_libraries(${test_name} PRIVATE inline_tests unity ${REPO_LIB} ) + + add_test(NAME ${test_name} COMMAND $) +endfunction() + +function(test_for_inline_tests file out_res) + file(READ ${file} file_content) + string(REGEX MATCH "AE_TEST_INLINE" matches ${file_content}) + if (NOT "s${matches}" STREQUAL "s") + set(${out_res} TRUE PARENT_SCOPE) + else() + set(${out_res} FALSE PARENT_SCOPE) + endif() +endfunction() + +function(generate_inline_tests lib_dir lib_name) + message(STATUS "Generating inline tests for [${lib_name}] at dir ${lib_dir}") + file(REAL_PATH ${lib_dir} REPO_DIR) + set(REPO_LIB ${lib_name}) + + file(GLOB_RECURSE files LIST_DIRECTORIES false "${REPO_DIR}/**/*.cpp" "${REPO_DIR}/**/*.h") + foreach(test_file ${files}) + test_for_inline_tests(${test_file} is_inline_test) + if(is_inline_test) + add_inline_test(${test_file}) + endif() + endforeach() +endfunction() diff --git a/tests/inline_tests/tests/crc.h b/tests/inline_tests/tests/crc.h new file mode 100644 index 00000000..c87367d9 --- /dev/null +++ b/tests/inline_tests/tests/crc.h @@ -0,0 +1,216 @@ +/* + * Copyright 2024 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_CRC_H_ +#define TESTS_CRC_H_ + +#include +#include +#include + +namespace inline_tests::crc32 { +struct result_t { + std::uint32_t value; + constexpr result_t(); + constexpr result_t(std::uint32_t); + explicit operator std::uint32_t() const; + auto operator==(const result_t&) const -> bool; +}; + +static const auto default_v = result_t(); + +// compile-time versions + +template +constexpr auto from_literal(const char (&_str)[LEN]) -> result_t; +constexpr auto from_string_view(std::string_view _str) -> result_t; +template +constexpr auto checksum_from_literal(const char (&_str)[LEN]) -> std::uint32_t; + +// run-time versions + +auto from_string(char const* _str, result_t _curr = result_t()) -> result_t; +auto from_buffer(const std::uint8_t* _buffer, size_t _cnt, + result_t _curr = result_t()) -> result_t; + +// ============== +// Implementation +// ============== + +// helpers + +namespace details { + +static const auto INITIAL_VALUE = std::uint32_t{0xFFffFFff}; +static const auto XOR_VALUE = std::uint32_t{0xFFffFFff}; + +template +struct aux; + +constexpr std::uint32_t table[256] = { + // polynomial = 0x04C11DB7 + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +template <> +struct aux<1, 0> { + static constexpr auto exec(const char (&)[1], result_t, result_t) + -> result_t { + return result_t(XOR_VALUE); + } +}; // Empty string case + +template +struct aux { + static constexpr auto exec(const char (&_str)[LEN], result_t _curr, result_t) + -> result_t { + return aux::exec( + // 1. String itself + _str, + + // 2. Add '_str[INDEX]' to the current crc value + result_t{table[static_cast(_curr.value) ^ + static_cast(_str[INDEX])] ^ + (_curr.value >> 8)}, + + // 3. Pass through the unmodified current crc32 (special case of "end of + // string") + _curr); + } +}; // General case + +template +struct aux { + static constexpr auto exec(const char (&)[LEN], result_t, result_t _prev) + -> result_t { + return _prev; // we don't want to include '\0' at the end of literal + // strings + } +}; // End of string case + +} // namespace details + +// actual implementation + +template +constexpr auto from_literal(const char (&_str)[LEN]) -> result_t { + return details::aux::exec(_str, result_t(), result_t()); +} + +constexpr auto from_string_view(std::string_view _str) -> result_t { + auto _curr = result_t{}; + for (auto const& _char : _str) { + _curr.value = (_curr.value >> 8) ^ + details::table[static_cast(_curr.value) ^ + static_cast(_char)]; + } + return _curr; +} + +template +constexpr auto checksum_from_literal(const char (&_str)[LEN]) -> std::uint32_t { + return from_literal(_str).value ^ details::XOR_VALUE; +} + +inline auto from_string(char const* _str, result_t _curr) -> result_t { + if (_str) { + while (*_str != 0) { + _curr.value = + (_curr.value >> 8) ^ + details::table[uint8_t(_curr.value) ^ std::uint8_t(*_str++)]; + } + } + + return _curr; +} + +inline auto from_buffer(const std::uint8_t* _buff, size_t _cnt, result_t _curr) + -> result_t { + if (_buff) { + for (auto i = 0u; i < _cnt; ++i) { + _curr.value = + (_curr.value >> 8) ^ + details::table[uint8_t(_curr.value) ^ std::uint8_t(*(_buff + i))]; + } + } + + return _curr; +} + +inline constexpr result_t::result_t() : value(details::INITIAL_VALUE) {} +inline constexpr result_t::result_t(uint32_t _val) : value(_val) {} +inline result_t::operator std::uint32_t() const { + return value ^ details::XOR_VALUE; +} +inline auto result_t::operator==(const result_t& _rhs) const -> bool { + return value == _rhs.value; +} + +// ============ +// Unit testing +// ============ +static_assert(checksum_from_literal("") == 0, + "crc32 unit test (empty string) failed!"); +static_assert(checksum_from_literal("A") == 0xD3D99E8B, + "crc32 unit test ('A') failed!"); +static_assert(checksum_from_literal("Hello world") == 0x8BD69E52, + "crc32 unit test ('Hello world') failed!"); + +} // namespace inline_tests::crc32 + +#endif // TESTS_CRC_H_ */ diff --git a/tests/inline_tests/tests/inline.h b/tests/inline_tests/tests/inline.h new file mode 100644 index 00000000..c0b01600 --- /dev/null +++ b/tests/inline_tests/tests/inline.h @@ -0,0 +1,52 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_INLINE_H_ +#define TESTS_INLINE_H_ + +#include + +#include "unity.h" + +#include "tests/crc.h" + +template +void setup_inline_test_function() {}; + +template +void teardown_inline_test_function() {}; + +template +void run_inline_test_function() {}; + +#define AE_SETUP_INLINE \ + template <> \ + inline void setup_inline_test_function< \ + ::inline_tests::crc32::checksum_from_literal(__FILE__)>() {} + +#define AE_TEARDOWN_INLINE \ + template <> \ + inline void teardown_inline_test_function< \ + ::inline_tests::crc32::checksum_from_literal(__FILE__)>() {} + +#define AE_TEST_INLINE \ + template <> \ + inline void run_inline_test_function< \ + ::inline_tests::crc32::checksum_from_literal(__FILE__)>() + +#define TEST(Function) RUN_TEST(Function) + +#endif // TESTS_INLINE_H_ diff --git a/tests/inline_tests/tests/main.cpp.in b/tests/inline_tests/tests/main.cpp.in new file mode 100644 index 00000000..eb86fc90 --- /dev/null +++ b/tests/inline_tests/tests/main.cpp.in @@ -0,0 +1,40 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * It's generated file for inline tests. + * \see tests/inline_tests/CMakeLists.txt + */ + +#include "unity.h" + +#define TEST_FILE "@TEST_FILE@" + +#include "tests/inline.h" +#include "@TEST_FILE@" + +inline constexpr auto kFileId = + inline_tests::crc32::checksum_from_literal(TEST_FILE); + +void setUp() { setup_inline_test_function(); } +void tearDown() { teardown_inline_test_function(); } + +int main() { + UNITY_BEGIN(); + TEST_MESSAGE("Run inline test for " TEST_FILE); + run_inline_test_function(); + return UNITY_END(); +} From cd5dd172335ebfc2c7e62b22d33cfc98f3fc7c7f Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Wed, 25 Feb 2026 17:22:40 +0500 Subject: [PATCH 03/11] add meta with tests --- CPPLINT.cfg | 1 + aether/actions/pipeline.h | 2 +- aether/api_protocol/api_class_impl.h | 8 +- aether/api_protocol/sub_api.h | 9 +- aether/meta/arg_at.h | 103 +++++++++++++ aether/meta/as_type.h | 73 +++++++++ aether/meta/function_signature.h | 54 +++++++ aether/meta/tag_invoke.h | 141 ++++++++++++++++++ aether/{types => meta}/type_list.h | 93 +++++------- aether/reflect/reflect_impl.h | 8 +- aether/stream_api/gates_stream.h | 2 +- aether/type_traits.h | 44 +----- aether/types/address.h | 1 + aether/types/small_function.h | 16 +- aether/types/variant_type.h | 24 +-- .../benches/send_message_delays/receiver.cpp | 4 +- tests/CMakeLists.txt | 5 + tests/test-meta/CMakeLists.txt | 37 +++++ tests/test-meta/main.cpp | 28 ++++ .../test-type-list.cpp | 12 +- tests/test-reflect/test-reflect.cpp | 18 +-- tests/test-tele/test-tele.cpp | 30 ++-- tests/test-types/CMakeLists.txt | 1 - tests/test-types/main.cpp | 2 - 24 files changed, 552 insertions(+), 164 deletions(-) create mode 100644 aether/meta/arg_at.h create mode 100644 aether/meta/as_type.h create mode 100644 aether/meta/function_signature.h create mode 100644 aether/meta/tag_invoke.h rename aether/{types => meta}/type_list.h (55%) create mode 100644 tests/test-meta/CMakeLists.txt create mode 100644 tests/test-meta/main.cpp rename tests/{test-types => test-meta}/test-type-list.cpp (93%) diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 408c8005..486bc5c4 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -14,6 +14,7 @@ filter=-readability/todo # this requered author to all todo filter=-readability/multiline_string # this prevents from using multiline strings +filter=-readability/braces # this prevents using concepts filter=-runtime/references # this forbids from using references as return value filter=-runtime/int # some libraries requires use long long int instead of sized int64_t filter=-build/c++17 # this prevents to use diff --git a/aether/actions/pipeline.h b/aether/actions/pipeline.h index d12c5c96..914b1ba9 100644 --- a/aether/actions/pipeline.h +++ b/aether/actions/pipeline.h @@ -281,7 +281,7 @@ class StageRunner { public: using type = - typename ActionType::Ret>::type; + typename ActionType::ret>::type; explicit StageRunner(TFunc&& factory) : factory_(std::move(factory)) {} diff --git a/aether/api_protocol/api_class_impl.h b/aether/api_protocol/api_class_impl.h index 7ce52c4b..04ba5261 100644 --- a/aether/api_protocol/api_class_impl.h +++ b/aether/api_protocol/api_class_impl.h @@ -192,9 +192,9 @@ struct LoadSelector; template static bool ApiLoadFactory(Api* api, MessageId message_id, ApiParser& parser, std::index_sequence) { - return ( - LoadSelector>::Load(api, message_id, parser) || - ...); + return (LoadSelector>::Load(api, message_id, + parser) || + ...); } template @@ -223,7 +223,7 @@ template static bool LoadFactoryImpl(Api* api, MessageId message_id, ApiParser& parser) { static_assert(HasApiMethods::value, "Api should provide ApiMethods"); using List = decltype(Api::ApiMethods()); - constexpr auto list_size = TypeListSize; + constexpr auto list_size = TypeListSize_v; return ApiLoadFactory( api, message_id, parser, std::make_index_sequence()); diff --git a/aether/api_protocol/sub_api.h b/aether/api_protocol/sub_api.h index 3299836a..70ff75e6 100644 --- a/aether/api_protocol/sub_api.h +++ b/aether/api_protocol/sub_api.h @@ -19,10 +19,10 @@ #include -#include "aether/type_traits.h" -#include "aether/types/type_list.h" +#include "aether/meta/type_list.h" #include "aether/reflect/reflect.h" #include "aether/types/small_function.h" +#include "aether/meta/function_signature.h" #include "aether/api_protocol/api_context.h" #include "aether/api_protocol/api_pack_parser.h" @@ -42,7 +42,8 @@ class SubApi { public: using Method = SmallFunction& api)>; - template ))> + template + requires(!std::same_as>) explicit SubApi(TFunc&& caller) : caller_{std::forward(caller)} {} explicit SubApi(Method caller) : caller_{std::move(caller)} {} @@ -58,7 +59,7 @@ class SubApi { template SubApi(TFunc&&) -> SubApi::Args>>>::type>; + std::decay_t::args>>>::type>; template class SubApiImpl { diff --git a/aether/meta/arg_at.h b/aether/meta/arg_at.h new file mode 100644 index 00000000..c1eb7266 --- /dev/null +++ b/aether/meta/arg_at.h @@ -0,0 +1,103 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AETHER_META_ARG_AT_H_ +#define AETHER_META_ARG_AT_H_ + +#include +#include + +namespace ae { +/** + * \brief Get I'th argument in args list. + */ +template +constexpr decltype(auto) VarAt([[maybe_unused]] T&& arg, + [[maybe_unused]] TArgs&&... args) { + static_assert(I <= sizeof...(args), "Index out of bounds"); + if constexpr (I == 0) { + return std::forward(arg); + } else { + return VarAt(std::forward(args)...); + } +} + +template +struct ArgAt { + static_assert(I <= sizeof...(Args), "Index out of bounds"); + static constexpr auto value = ArgAt::value; +}; + +template +struct ArgAt<0, Arg, Args...> { + static constexpr auto value = Arg; +}; + +template +static inline constexpr auto ArgAt_v = ArgAt::value; + +} // namespace ae + +#if AE_TESTS +# include "tests/inline.h" + +namespace test::arg_at_h { +inline void test_VarAt() { + constexpr int x = 42; + constexpr double y = 3.14; + constexpr bool z = true; + + static_assert(ae::VarAt<0>(x, y, z) == 42); + static_assert(ae::VarAt<1>(x, y, z) == 3.14); + static_assert(ae::VarAt<2>(x, y, z)); + + int a = 10; + float b = 3.14f; + bool c = true; + + TEST_ASSERT_EQUAL(10, ae::VarAt<0>(a, b, c)); + TEST_ASSERT_EQUAL_FLOAT(3.14f, ae::VarAt<1>(a, b, c)); + TEST_ASSERT_EQUAL(true, ae::VarAt<2>(a, b, c)); +} + +template +inline void testTemplateArgs() { + TEST_ASSERT_EQUAL(10, (ae::ArgAt_v<0, Args...>)); + TEST_ASSERT_EQUAL_FLOAT(3.14f, (ae::ArgAt_v<1, Args...>)); + TEST_ASSERT_EQUAL(true, (ae::ArgAt_v<2, Args...>)); +} + +inline void test_ArgAt() { + constexpr int x = 42; + constexpr double y = 3.14; + constexpr bool z = true; + + static_assert(ae::ArgAt_v<0, x, y, z> == 42); + static_assert(ae::ArgAt_v<1, x, y, z> == 3.14); + static_assert(ae::ArgAt_v<2, x, y, z>); + + testTemplateArgs<10, 3.14f, true>(); +} + +} // namespace test::arg_at_h + +AE_TEST_INLINE { + RUN_TEST(test::arg_at_h::test_VarAt); + RUN_TEST(test::arg_at_h::test_ArgAt); +} +#endif + +#endif // AETHER_META_ARG_AT_H_ diff --git a/aether/meta/as_type.h b/aether/meta/as_type.h new file mode 100644 index 00000000..904cda23 --- /dev/null +++ b/aether/meta/as_type.h @@ -0,0 +1,73 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AETHER_META_AS_TYPE_H_ +#define AETHER_META_AS_TYPE_H_ + +#include +#include + +namespace ae { +// AS rvalue +template +T&& as_type(U&& u) + requires std::is_same_v +{ + return static_cast(std::forward(u)); +} + +// AS lvalue +template +T& as_type(U& u) { + return static_cast(u); +} + +// AS const lvalue +template +T const& as_type(U const& u) { + return static_cast(u); +} +} // namespace ae + +#ifdef AE_TESTS + +# include "tests/inline.h" + +namespace test::as_type_h { +using ae::as_type; + +struct Base {}; +struct Derived : Base {}; + +inline void test_AsType() { + Derived d; + decltype(auto) b1 = as_type(d); + static_assert(std::is_same_v); + + decltype(auto) b2 = as_type(std::as_const(d)); + static_assert(std::is_same_v); + + decltype(auto) b3 = as_type(std::move(d)); + static_assert(std::is_same_v); + + TEST_PASS(); +} +} // namespace test::as_type_h + +AE_TEST_INLINE { TEST(test::as_type_h::test_AsType); } + +#endif +#endif // AETHER_META_AS_TYPE_H_ diff --git a/aether/meta/function_signature.h b/aether/meta/function_signature.h new file mode 100644 index 00000000..8f6b76b9 --- /dev/null +++ b/aether/meta/function_signature.h @@ -0,0 +1,54 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AETHER_META_FUNCTION_SIGNATURE_H_ +#define AETHER_META_FUNCTION_SIGNATURE_H_ + +#include + +#include "aether/meta/type_list.h" + +namespace ae { +template +struct FunctionSignatureImpl { + using ret = Ret; + using args = TypeList; + using signature = ret(args...); + using func_ptr = ret (*)(args...); +}; + +template +auto GetFunctionSignature(R (*)(Args...)) -> FunctionSignatureImpl; + +template +auto GetFunctionSignature(R (C::*)(Args...)) + -> FunctionSignatureImpl; + +template +auto GetFunctionSignature(R (C::*)(Args...) const) + -> FunctionSignatureImpl; + +template +auto GetFunctionSignature(Callable) + -> decltype(GetFunctionSignature(&Callable::operator())); + +template +struct FunctionSignature + : decltype(GetFunctionSignature(std::declval())){}; + +} // namespace ae + +#endif // AETHER_META_FUNCTION_SIGNATURE_H_ diff --git a/aether/meta/tag_invoke.h b/aether/meta/tag_invoke.h new file mode 100644 index 00000000..05f8fad0 --- /dev/null +++ b/aether/meta/tag_invoke.h @@ -0,0 +1,141 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AETHER_META_TAG_INVOKE_H_ +#define AETHER_META_TAG_INVOKE_H_ + +#include + +namespace ae { + +namespace _tag_invoke { +void tag_invoke(); + +struct fn_ { + template + constexpr auto operator()(Cpo&& cpo, Args&&... args) const + noexcept(noexcept(tag_invoke(std::forward(cpo), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(cpo), + std::forward(args)...)) { + return tag_invoke(std::forward(cpo), std::forward(args)...); + } +}; + +template +using tag_invoke_result_t = + decltype(tag_invoke(std::declval(), std::declval()...)); + +template > +std::true_type TestTagInvocable(int) noexcept( + noexcept(tag_invoke(std::declval(), std::declval()...))); + +template +std::false_type TestTagInvocable(...); + +} // namespace _tag_invoke +inline constexpr auto tag_invoke = _tag_invoke::fn_{}; + +using _tag_invoke::tag_invoke_result_t; +using _tag_invoke::TestTagInvocable; + +template +inline constexpr bool TagInvocable_v = + decltype(TestTagInvocable(0))::value; + +template +concept TagInvocable = + TagInvocable_v || TagInvocable_v || + TagInvocable_v || TagInvocable_v; + +/** + * \brief Helper struct to define cpo tag + */ +template +using tag_t = std::decay_t; + +} // namespace ae + +#ifdef AE_TESTS +# include "tests/inline.h" + +namespace test::tag_invoke_h { +inline constexpr struct TestCpo { + void operator()(ae::TagInvocable auto& t) const { + ae::tag_invoke(*this, t); + } +} test_cpo{}; + +inline constexpr struct TestGetInt { + int operator()(ae::TagInvocable auto& t) const { + return ae::tag_invoke(*this, t); + } +} test_get_int{}; + +struct Foo { + bool& invoked; + + friend void tag_invoke(ae::tag_t, Foo& foo) noexcept { + foo.invoked = true; + } +}; + +struct Bar { + int value; + + friend int tag_invoke(ae::tag_t, Bar const& bar) noexcept { + return bar.value; + } +}; + +inline void test_TagInvoke() { + using TestCpoType = ae::tag_t; + static_assert(std::is_same_v); + using TestGetIntType = ae::tag_t; + static_assert(std::is_same_v); + + using foo_res = ae::tag_invoke_result_t, Foo&>; + static_assert(std::is_void_v); + static_assert(ae::TagInvocable_v, Foo&>); + static_assert(!ae::TagInvocable_v, Foo&>); + + using bar_res = ae::tag_invoke_result_t, Bar const&>; + static_assert(std::is_same_v); + static_assert(ae::TagInvocable_v, Bar const&>); + static_assert(!ae::TagInvocable_v, Bar const&>); + + static_assert(ae::TagInvocable>); + static_assert(!ae::TagInvocable>); + static_assert(ae::TagInvocable>); + static_assert(!ae::TagInvocable>); + + bool invoked{}; + Foo foo{invoked}; + test_cpo(foo); + TEST_ASSERT_TRUE(invoked); + + Bar bar{42}; + auto value = test_get_int(bar); + TEST_ASSERT_EQUAL(42, value); +} +} // namespace test::tag_invoke_h + +AE_TEST_INLINE { TEST(test::tag_invoke_h::test_TagInvoke); } + +#endif + +#endif // AETHER_META_TAG_INVOKE_H_ diff --git a/aether/types/type_list.h b/aether/meta/type_list.h similarity index 55% rename from aether/types/type_list.h rename to aether/meta/type_list.h index 973a1809..377a609f 100644 --- a/aether/types/type_list.h +++ b/aether/meta/type_list.h @@ -1,5 +1,5 @@ /* - * Copyright 2025 Aethernet Inc. + * Copyright 2026 Aethernet Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,13 @@ * limitations under the License. */ -#ifndef AETHER_TYPES_TYPE_LIST_H_ -#define AETHER_TYPES_TYPE_LIST_H_ +#ifndef AETHER_META_TYPE_LIST_H_ +#define AETHER_META_TYPE_LIST_H_ #include #include namespace ae { -/** - * \brief List of types used in metaprogramming. - */ template struct TypeList { TypeList() = default; @@ -32,65 +29,49 @@ struct TypeList { }; template -TypeList(T&&...) -> TypeList; - -template -struct TypeListSizeImpl; - -template -struct TypeListSizeImpl> { - static constexpr std::size_t value = sizeof...(Ts); -}; - -template -static constexpr std::size_t TypeListSize = TypeListSizeImpl::value; +TypeList(T...) -> TypeList; -/** - * \brief Join two type lists. - */ -template -constexpr auto JoinTypeLists(TypeList, TypeList) - -> TypeList; - -template -using JoinLists = - decltype(JoinTypeLists(std::declval(), std::declval())); +template +struct TypeAt; -/** - * \brief Get I'th type in template type list. - */ template -struct TypeAtImpl { - using type = typename TypeAtImpl::type; +struct TypeAt> { + using type = typename TypeAt>::type; }; template -struct TypeAtImpl<0, T, Ts...> { +struct TypeAt<0, TypeList> { using type = T; }; -template -struct TypeAt; +template +using TypeAt_t = typename TypeAt::type; -template -struct TypeAt> { - using type = typename TypeAtImpl::type; +template +struct TypeListSize; + +template +struct TypeListSize> { + static constexpr std::size_t value = sizeof...(Ts); }; -template -using TypeAtT = typename TypeAt::type; +template +static inline constexpr std::size_t TypeListSize_v = TypeListSize::value; /** - * \brief Get I'th argument in args list. + * \brief Join two type lists. */ -template -constexpr auto&& ArgAt(T&& arg, TArgs&&... args) { - if constexpr (I == 0) { - return std::forward(arg); - } else { - return ArgAt(std::forward(args)...); - } -} +template +struct JoinedTypeList; + +template +struct JoinedTypeList, TypeList> { + using type = TypeList; +}; + +template +using JoinedTypeList_t = + typename JoinedTypeList, std::decay_t>::type; /** * \brief Pass type list as template parameters to template T @@ -103,13 +84,16 @@ struct TypeListToTemplate> { using type = T; }; -template +/** + * \brief Revers type list + */ +template struct ReversTypeList; template struct ReversTypeList> { - using type = - JoinLists>::type, TypeList>; + using type = JoinedTypeList_t>::type, + TypeList>; }; template @@ -118,5 +102,4 @@ struct ReversTypeList> { }; } // namespace ae - -#endif // AETHER_TYPES_TYPE_LIST_H_ +#endif // AETHER_META_TYPE_LIST_H_ diff --git a/aether/reflect/reflect_impl.h b/aether/reflect/reflect_impl.h index 7d32aca0..da4cccb2 100644 --- a/aether/reflect/reflect_impl.h +++ b/aether/reflect/reflect_impl.h @@ -22,7 +22,7 @@ #include #include "aether/common.h" -#include "aether/types/type_list.h" // IWYU pragma: keep +#include "aether/meta/type_list.h" // IWYU pragma: keep namespace ae::reflect { namespace reflect_internal { @@ -89,7 +89,7 @@ struct FieldList { */ template static constexpr decltype(auto) get(U&& obj) { - return TypeAtT::get(std::forward(obj)); + return TypeAt_t::get(std::forward(obj)); } }; @@ -250,7 +250,7 @@ struct IsReflectable, decltype(fields)>; \ + ::ae::JoinedTypeList_t<::ae::TypeList, decltype(fields)>; \ using FL = typename ::ae::TypeListToTemplate< \ ::ae::reflect::reflect_internal::FieldList, TypeFieldTypeList>::type; \ return FL{}; \ @@ -267,7 +267,7 @@ struct IsReflectable, decltype(fields)>; \ + ::ae::JoinedTypeList_t<::ae::TypeList, decltype(fields)>; \ using FL = typename ::ae::TypeListToTemplate< \ ::ae::reflect::reflect_internal::FieldList, TypeFieldTypeList>::type; \ return FL{}; \ diff --git a/aether/stream_api/gates_stream.h b/aether/stream_api/gates_stream.h index e2bbee1e..7ece3217 100644 --- a/aether/stream_api/gates_stream.h +++ b/aether/stream_api/gates_stream.h @@ -21,7 +21,7 @@ #include #include -#include "aether/types/type_list.h" +#include "aether/meta/type_list.h" #include "aether/stream_api/istream.h" #include "aether/stream_api/gate_trait.h" #include "aether/stream_api/tied_gates.h" diff --git a/aether/type_traits.h b/aether/type_traits.h index 68bd9f69..fd43ef82 100644 --- a/aether/type_traits.h +++ b/aether/type_traits.h @@ -23,7 +23,8 @@ #include #include -#include "aether/types/type_list.h" +#include "aether/meta/arg_at.h" +#include "aether/meta/type_list.h" namespace ae { @@ -44,7 +45,7 @@ template decltype(auto) ApplyByIndices(TFunc&& func, std::index_sequence, TArgs&&... args) { return std::forward(func)( - ArgAt(std::forward(args)...)...); + VarAt(std::forward(args)...)...); } } // namespace _internal @@ -179,45 +180,6 @@ struct IsFunctionPtr< std::void_t(std::declval()))>> : std::true_type {}; -template -struct FunctionSignatureImpl; - -template -struct FunctionSignatureImpl { - using Args = TypeList; - using Ret = TRet; - using Signature = TRet(TArgs...); - using FuncPtr = TRet (*)(TArgs...); -}; - -template -auto GetSignatureImpl(TRes (*)(TArgs...)) - -> FunctionSignatureImpl; - -template -auto GetSignatureImpl(TRes (TClass::*)(TArgs...) const) - -> FunctionSignatureImpl; - -template -auto GetSignatureImpl(TRes (TClass::*)(TArgs...)) - -> FunctionSignatureImpl; - -template -auto GetSignatureImpl(TCallable) - -> decltype(GetSignatureImpl(&TCallable::operator())); - -/** - * \brief Get a signature of functor object, or function pointer. - */ -template ()))> -struct FunctionSignature { - using Args = typename FuncSignatureImp::Args; - using Ret = typename FuncSignatureImp::Ret; - using Signature = typename FuncSignatureImp::Signature; - using FuncPtr = typename FuncSignatureImp::FuncPtr; -}; - template struct ArraySize; diff --git a/aether/types/address.h b/aether/types/address.h index c2474375..f8cf2e8e 100644 --- a/aether/types/address.h +++ b/aether/types/address.h @@ -77,6 +77,7 @@ omstream& operator<<(omstream& s, IpV6Addr const& ipv6) { struct NamedAddr { AE_REFLECT_MEMBERS(name) + std::string name; }; diff --git a/aether/types/small_function.h b/aether/types/small_function.h index 647397b0..f6007514 100644 --- a/aether/types/small_function.h +++ b/aether/types/small_function.h @@ -19,11 +19,11 @@ #include #include +#include #include -#include "aether/common.h" -#include "aether/type_traits.h" #include "aether/types/aligned_storage.h" +#include "aether/meta/function_signature.h" namespace ae { namespace small_function_internal { @@ -151,15 +151,16 @@ class SmallFunction { } } - template < - typename TFunctor, - AE_REQUIRERS((IsFunctor, TRet(TArgs...)>)), - AE_REQUIRERS_NOT((std::is_same>))> + template + requires(requires { + { std::invocable }; + { std::same_as, TRet> }; + { !std::same_as, SmallFunction> }; + }) SmallFunction(TFunctor&& functor) noexcept : vtable_{&small_function_internal::VTableForT, TRet, TArgs...>} { using Type = std::decay_t; - static_assert(sizeof(Type) > 0, "TFunctor must be a complete type"); static_assert(sizeof(Type) <= Size, "TFunctor must fit into storage"); new (storage_.data()) Type{std::forward(functor)}; @@ -231,6 +232,7 @@ class SmallFunction { void Destroy() noexcept { vtable_->manage(storage_.data(), nullptr, Operation::kDestroy); } + void Move(Storage& src, Storage& dst) noexcept { vtable_->manage(src.data(), dst.data(), Operation::kMove); } diff --git a/aether/types/variant_type.h b/aether/types/variant_type.h index 46ce4f44..dba9a6a0 100644 --- a/aether/types/variant_type.h +++ b/aether/types/variant_type.h @@ -24,7 +24,7 @@ #include #include "aether/mstream.h" -#include "aether/types/type_list.h" +#include "aether/meta/type_list.h" namespace ae { template @@ -55,7 +55,7 @@ class VariantType : public std::variant { ( [&]() { if (order == Is) { - res = TypeAtT>::Index; + res = TypeAt_t>::Index; } }(), ...); @@ -68,7 +68,7 @@ class VariantType : public std::variant { std::size_t res{}; ( [&]() { - if (index == TypeAtT>::Index) { + if (index == TypeAt_t>::Index) { res = Is; } }(), @@ -77,7 +77,7 @@ class VariantType : public std::variant { } template - static bool LoadElement(Stream &stream, Variant &var) { + static bool LoadElement(Stream& stream, Variant& var) { using T = std::variant_alternative_t; T t{}; stream >> t; @@ -86,8 +86,8 @@ class VariantType : public std::variant { } template - static void Load(Stream &stream, std::size_t order, Variant &var, - std::index_sequence const &) { + static void Load(Stream& stream, std::size_t order, Variant& var, + std::index_sequence const&) { (std::invoke([&]() { if (order == Is) { LoadElement(stream, var); @@ -97,8 +97,8 @@ class VariantType : public std::variant { } template - static void Save(Stream &stream, std::size_t order, Variant const &var, - std::index_sequence const &) { + static void Save(Stream& stream, std::size_t order, Variant const& var, + std::index_sequence const&) { (std::invoke([&]() { if (order == Is) { stream << std::get(var); @@ -108,7 +108,7 @@ class VariantType : public std::variant { } template - constexpr auto const &GetImpl(std::index_sequence const &) const { + constexpr auto const& GetImpl(std::index_sequence const&) const { if constexpr (std::is_same_v>) { return std::get(*this); @@ -125,14 +125,14 @@ class VariantType : public std::variant { } template - constexpr auto const &Get() const { + constexpr auto const& Get() const { static_assert((std::is_same_v || ...), "Type not found"); return GetImpl(std::make_index_sequence()); } template - friend imstream operator>>(imstream &is, VariantType &v) { + friend imstream operator>>(imstream& is, VariantType& v) { index_type index{}; is >> index; auto order = @@ -143,7 +143,7 @@ class VariantType : public std::variant { } template - friend omstream operator<<(omstream &os, VariantType const &v) { + friend omstream operator<<(omstream& os, VariantType const& v) { auto order = v.index(); os << GetIndexByOrder(order, std::make_index_sequence()); diff --git a/examples/benches/send_message_delays/receiver.cpp b/examples/benches/send_message_delays/receiver.cpp index 26caca7b..1bdffb90 100644 --- a/examples/benches/send_message_delays/receiver.cpp +++ b/examples/benches/send_message_delays/receiver.cpp @@ -19,7 +19,7 @@ #include #include -#include "aether/types/type_list.h" +#include "aether/meta/arg_at.h" #include "aether/client_messages/p2p_safe_message_stream.h" #include "send_message_delays/api/bench_delays_api.h" @@ -86,7 +86,7 @@ ActionPtr Receiver::CreateBenchAction(TEvent event, receiver_action_ = ActionPtr{action_context_, count}; event.Subscribe([ra{receiver_action_}](auto&&... args) mutable { if (ra) { - ra->Receive(ArgAt<0>(std::forward(args)...)); + ra->Receive(VarAt<0>(std::forward(args)...)); } }); return receiver_action_; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d2f4c0a2..837450ab 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,10 +43,15 @@ if (NOT TARGET gcem) add_subdirectory("${ROOT_DIR}/third_party/gcem" "gcem") endif() +include(inline_tests/inline_tests.cmake) +add_subdirectory(inline_tests) +generate_inline_tests(${CMAKE_CURRENT_LIST_DIR}/../aether aether) + #tests add_subdirectory(test-tele) add_subdirectory(test-tele-statistics) add_subdirectory(test-types) +add_subdirectory(test-meta) add_subdirectory(test-object-system) add_subdirectory(test-api-protocol) add_subdirectory(test-actions) diff --git a/tests/test-meta/CMakeLists.txt b/tests/test-meta/CMakeLists.txt new file mode 100644 index 00000000..e0e94a80 --- /dev/null +++ b/tests/test-meta/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright 2026 Aethernet Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required( VERSION 3.16 ) + +list(APPEND test_srcs + main.cpp + test-type-list.cpp +) + +if(NOT CM_PLATFORM) + project(test-meta LANGUAGES CXX) + + add_executable(${PROJECT_NAME}) + target_sources(${PROJECT_NAME} PRIVATE ${test_srcs}) + # for aether + target_include_directories(${PROJECT_NAME} PRIVATE ${ROOT_DIR}) + target_link_libraries(${PROJECT_NAME} PRIVATE unity) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(${PROJECT_NAME} PUBLIC /Zc:preprocessor) + endif() + + add_test(NAME ${PROJECT_NAME} COMMAND $) +else() + message(WARNING "Not implemented for ${CM_PLATFORM}") +endif() diff --git a/tests/test-meta/main.cpp b/tests/test-meta/main.cpp new file mode 100644 index 00000000..d157ef4c --- /dev/null +++ b/tests/test-meta/main.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +void setUp() {} +void tearDown() {} + +extern int test_type_list(); + +int main() { + int res{}; + res += test_type_list(); + return res; +} diff --git a/tests/test-types/test-type-list.cpp b/tests/test-meta/test-type-list.cpp similarity index 93% rename from tests/test-types/test-type-list.cpp rename to tests/test-meta/test-type-list.cpp index 75ff4eb5..5b744e44 100644 --- a/tests/test-types/test-type-list.cpp +++ b/tests/test-meta/test-type-list.cpp @@ -16,7 +16,7 @@ #include -#include "aether/types/type_list.h" +#include "aether/meta/type_list.h" #if defined __GNUC__ # define AE_FUNC_NAME __PRETTY_FUNCTION__ @@ -67,7 +67,7 @@ struct Worker { void work() { auto const* old_test_name = Unity.CurrentTestName; Unity.CurrentTestName = AE_FUNC_NAME; - using type_at = TypeAtT; + using type_at = TypeAt_t; TEST_ASSERT_TRUE((std::is_same_v, type_at>)); Unity.CurrentTestName = old_test_name; @@ -80,8 +80,8 @@ struct WorkerReverse { void work() { auto const* old_test_name = Unity.CurrentTestName; Unity.CurrentTestName = AE_FUNC_NAME; - constexpr auto kLastN = TypeListSize - 1; - using type_at = TypeAtT; + constexpr auto kLastN = TypeListSize_v - 1; + using type_at = TypeAt_t; TEST_ASSERT_TRUE((std::is_same_v, type_at>)); Unity.CurrentTestName = old_test_name; @@ -93,7 +93,7 @@ void TestNTypes() { auto type_list = MakeTypeList(); using TL = decltype(type_list); - TEST_ASSERT_EQUAL(N, TypeListSize); + TEST_ASSERT_EQUAL(N, TypeListSize_v); ForTypes<(N / 20) + 1>(Worker{}, type_list); Worker{}.template work(); } @@ -104,7 +104,7 @@ void TestNTypesReverse() { using TL = decltype(type_list); using RTL = typename ReversTypeList::type; - TEST_ASSERT_EQUAL(N, TypeListSize); + TEST_ASSERT_EQUAL(N, TypeListSize_v); ForTypes<(N / 20) + 1>(WorkerReverse{}, type_list); Worker{}.template work(); } diff --git a/tests/test-reflect/test-reflect.cpp b/tests/test-reflect/test-reflect.cpp index 3c87e65c..5a87a0e7 100644 --- a/tests/test-reflect/test-reflect.cpp +++ b/tests/test-reflect/test-reflect.cpp @@ -18,7 +18,7 @@ #include -#include "aether/types/type_list.h" +#include "aether/meta/arg_at.h" #include "aether/reflect/reflect.h" namespace ae::test_reflect { @@ -78,9 +78,9 @@ void test_FooApply() { auto foo_reflect = reflect::Reflection{foo}; bool applied = false; foo_reflect.Apply([&](auto const&... fields) { - TEST_ASSERT_EQUAL(1, ArgAt<0>(fields...)); - TEST_ASSERT_EQUAL_FLOAT(12.42f, ArgAt<1>(fields...)); - TEST_ASSERT_EQUAL_STRING("Hello", ArgAt<2>(fields...).c_str()); + TEST_ASSERT_EQUAL(1, VarAt<0>(fields...)); + TEST_ASSERT_EQUAL_FLOAT(12.42f, VarAt<1>(fields...)); + TEST_ASSERT_EQUAL_STRING("Hello", VarAt<2>(fields...).c_str()); applied = true; }); TEST_ASSERT(applied); @@ -94,12 +94,12 @@ void test_TwoLevelReflectable() { TEST_ASSERT_EQUAL(12, tlr_reflect.get<0>()); tlr_reflect.Apply([](auto const&... fields) { - TEST_ASSERT_EQUAL(12, ArgAt<0>(fields...)); - auto foo_refl = reflect::Reflection(ArgAt<1>(fields...)); + TEST_ASSERT_EQUAL(12, VarAt<0>(fields...)); + auto foo_refl = reflect::Reflection(VarAt<1>(fields...)); foo_refl.Apply([](auto const&... foo_fields) { - TEST_ASSERT_EQUAL(42, ArgAt<0>(foo_fields...)); - TEST_ASSERT_EQUAL_FLOAT(65.13F, ArgAt<1>(foo_fields...)); - TEST_ASSERT_EQUAL_STRING("World", ArgAt<2>(foo_fields...).c_str()); + TEST_ASSERT_EQUAL(42, VarAt<0>(foo_fields...)); + TEST_ASSERT_EQUAL_FLOAT(65.13F, VarAt<1>(foo_fields...)); + TEST_ASSERT_EQUAL_STRING("World", VarAt<2>(foo_fields...).c_str()); }); }); } diff --git a/tests/test-tele/test-tele.cpp b/tests/test-tele/test-tele.cpp index b5610aa2..eb223be6 100644 --- a/tests/test-tele/test-tele.cpp +++ b/tests/test-tele/test-tele.cpp @@ -43,7 +43,7 @@ #define AETHER_TELE_TELE_H_ -#include "aether/types/type_list.h" +#include "aether/meta/type_list.h" #include "aether/tele/sink.h" #include "aether/tele/tags.h" @@ -162,24 +162,24 @@ template struct TeleConfig { - static constexpr bool kCountMetrics = ArgAt<0>(args...); - static constexpr bool kTimeMetrics = ArgAt<1>(args...); - static constexpr bool kIndexLogs = ArgAt<2>(args...); - static constexpr bool kStartTimeLogs = ArgAt<3>(args...); - static constexpr bool kLevelModuleLogs = ArgAt<4>(args...); - static constexpr bool kLocationLogs = ArgAt<5>(args...); - static constexpr bool kNameLogs = ArgAt<6>(args...); - static constexpr bool kBlobLogs = ArgAt<7>(args...); + static constexpr bool kCountMetrics = ArgAt_v<0, args...>; + static constexpr bool kTimeMetrics = ArgAt_v<1, args...>; + static constexpr bool kIndexLogs = ArgAt_v<2, args...>; + static constexpr bool kStartTimeLogs = ArgAt_v<3, args...>; + static constexpr bool kLevelModuleLogs = ArgAt_v<4, args...>; + static constexpr bool kLocationLogs = ArgAt_v<5, args...>; + static constexpr bool kNameLogs = ArgAt_v<6, args...>; + static constexpr bool kBlobLogs = ArgAt_v<7, args...>; }; template struct EnvConfig { - static constexpr bool kCompiler = ArgAt<0>(args...); - static constexpr bool kPlatformType = ArgAt<1>(args...); - static constexpr bool kCompilationOptions = ArgAt<2>(args...); - static constexpr bool kLibraryVersion = ArgAt<3>(args...); - static constexpr bool kApiVersion = ArgAt<4>(args...); - static constexpr bool kCpuType = ArgAt<5>(args...); + static constexpr bool kCompiler = ArgAt_v<0, args...>; + static constexpr bool kPlatformType = ArgAt_v<1, args...>; + static constexpr bool kCompilationOptions = ArgAt_v<2, args...>; + static constexpr bool kLibraryVersion = ArgAt_v<3, args...>; + static constexpr bool kApiVersion = ArgAt_v<4, args...>; + static constexpr bool kCpuType = ArgAt_v<5, args...>; }; template diff --git a/tests/test-types/CMakeLists.txt b/tests/test-types/CMakeLists.txt index 5605efd0..ca2f707f 100644 --- a/tests/test-types/CMakeLists.txt +++ b/tests/test-types/CMakeLists.txt @@ -23,7 +23,6 @@ list(APPEND test_srcs test-static-map.cpp test-statistics-counter.cpp test-aligned-storage.cpp - test-type-list.cpp test-uid.cpp test-small-function.cpp test-nullable-type.cpp diff --git a/tests/test-types/main.cpp b/tests/test-types/main.cpp index 5b9a30f6..9cc884fb 100644 --- a/tests/test-types/main.cpp +++ b/tests/test-types/main.cpp @@ -26,7 +26,6 @@ extern int test_span(); extern int test_static_map(); extern int test_statistics_counter(); extern int test_aligned_storage(); -extern int test_type_list(); extern int test_uid(); extern int test_small_function(); extern int test_nullable_type(); @@ -41,7 +40,6 @@ int main() { res += test_static_map(); res += test_statistics_counter(); res += test_aligned_storage(); - res += test_type_list(); res += test_uid(); res += test_small_function(); res += test_nullable_type(); From a401ac63215b064a5d1654a87befa8df5c1d33c7 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Wed, 25 Feb 2026 17:53:21 +0500 Subject: [PATCH 04/11] add result type --- aether/types/result.h | 273 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 aether/types/result.h diff --git a/aether/types/result.h b/aether/types/result.h new file mode 100644 index 00000000..f4a3fa65 --- /dev/null +++ b/aether/types/result.h @@ -0,0 +1,273 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AETHER_TYPES_RESULT_H_ +#define AETHER_TYPES_RESULT_H_ + +#include +#include +#include +#include +#include + +namespace ae { +template +class Result; + +template +struct IsResultType : std::false_type {}; +template +struct IsResultType> : std::true_type {}; +template +static inline constexpr bool IsResultType_v = IsResultType::value; + +template +concept ResultType = requires(T t) { + { IsResultType_v }; + { std::same_as }; + { std::same_as }; +}; + +template +struct Ok { + explicit Ok(T v) : value{std::move(v)} {} + [[no_unique_address]] T value; +}; + +template +struct Error { + explicit Error(E v) : error{std::move(v)} {} + [[no_unique_address]] E error; +}; + +template +class Result { + public: + using value_type = T; + using error_type = E; + + template + requires(std::is_same_v) + explicit Result(U&& value) : storage_{std::forward(value)} {} + + template + requires(std::is_same_v) + explicit Result(EU&& error) : storage_{std::forward(error)} {} + + // made implicit intentionally + Result(Ok&& ok) : storage_{std::move(ok.value)} {} + // made implicit intentionally + Result(Error&& error) : storage_{std::move(error.error)} {} + + // TODO: add monadic operations + + bool IsOk() const noexcept { return storage_.index() == 0; } + bool IsErr() const noexcept { return storage_.index() == 1; } + + auto& value() & noexcept { + assert(IsOk()); + return *get_value(); + } + auto const& value() const& noexcept { + assert(IsOk()); + return *get_value(); + } + auto&& value() && noexcept { + assert(IsOk()); + return std::move(*get_value()); + } + + auto& error() & noexcept { + assert(IsErr()); + return *get_error(); + } + auto const& error() const& noexcept { + assert(IsErr()); + return *get_error(); + } + auto&& error() && noexcept { + assert(IsErr()); + return std::move(*get_error()); + } + + template > + requires(requires { + // F must return result type + { IsResultType_v }; + { ResultType }; + }) + auto Then(F&& f) && -> typename std::invoke_result_t { + if (IsOk()) { + return std::invoke(std::forward(f), std::move(*get_value())); + } + return Error{std::move(*get_error())}; + } + + template > + requires(requires { + // F must return result type + { IsResultType_v }; + { ResultType }; + }) + auto Then(F&& f) && -> std::invoke_result_t { + if (IsOk()) { + return std::invoke(std::forward(f)); + } + return Error{std::move(*get_error())}; + } + + template > + requires(requires { + // FE must return result type + { IsResultType_v }; + { ResultType }; + }) + auto Else(FE&& f) && -> typename std::invoke_result_t { + if (IsErr()) { + return std::invoke(std::forward(f), std::move(*get_error())); + } + return Ok{std::move(*get_value())}; + } + + template > + requires(requires { + // FE must return result type + { IsResultType_v }; + { ResultType }; + }) + auto Else(FE&& f) && -> typename std::invoke_result_t { + if (IsErr()) { + return std::invoke(std::forward(f)); + } + return Ok{std::move(*get_value())}; + } + + private: + value_type* get_value() & { return std::get_if<0>(&storage_); } + value_type* get_value() const& { return std::get_if<0>(&storage_); } + error_type* get_error() & { return std::get_if<1>(&storage_); } + error_type* get_error() const& { return std::get_if<1>(&storage_); } + + std::variant storage_; +}; +} // namespace ae + +#define TRY_VALUE(VAR_NAME, ...) \ + auto _RES_##VAR_NAME = __VA_ARGS__; \ + if (_RES_##VAR_NAME.IsErr()) { \ + return ::ae::Error{std::move(_RES_##VAR_NAME).error()}; \ + } \ + auto VAR_NAME = _RES_##VAR_NAME.value() + +#define TRY_RESULT(...) \ + { \ + auto _RES_ = __VA_ARGS__; \ + if (_RES_.IsErr()) { \ + return ::ae::Error{std::move(_RES_).error()}; \ + } \ + } + +#if AE_TESTS + +# include "tests/inline.h" + +namespace test::result_h_ { +using ae::Error; +using ae::Ok; +using ae::Result; + +struct G {}; +struct E {}; + +inline void test_Result() { + auto res = Result{G{}}; + // check if concept works + static_assert(ae::ResultType); + TEST_ASSERT_TRUE(res.IsOk()); + + auto res_e = Result{E{}}; + TEST_ASSERT_TRUE(res_e.IsErr()); +} + +inline void test_Monadic() { + auto ret_good = []() -> Result { return Ok{G{}}; }; + auto ret_bad = []() -> Result { return Error{E{}}; }; + + auto res_g = ret_good().Then(ret_good); + TEST_ASSERT_TRUE(res_g.IsOk()); + + auto res_e = ret_good().Then(ret_bad); + TEST_ASSERT_TRUE(res_e.IsErr()); + + auto res_g2 = ret_bad().Else(ret_good); + TEST_ASSERT_TRUE(res_g2.IsOk()); + auto res_e2 = ret_bad().Else(ret_bad); + TEST_ASSERT_TRUE(res_e2.IsErr()); + + auto res_cg = ret_good().Then(ret_good).Else(ret_bad); + TEST_ASSERT_TRUE(res_cg.IsOk()); + auto res_cg2 = ret_good().Then(ret_bad).Else(ret_good); + TEST_ASSERT_TRUE(res_cg2.IsOk()); + + auto res_ce = ret_bad().Then(ret_good).Else(ret_bad); + TEST_ASSERT_TRUE(res_ce.IsErr()); + auto res_ce2 = ret_bad().Else(ret_good).Else(ret_bad); + TEST_ASSERT_TRUE(res_ce2.IsOk()); +} + +inline void test_Macros() { + auto try_value = [](int v, int n) -> Result { + if (v > n) { + return Ok{G{}}; + } + return Error{E{}}; + }; + + auto test_good = [&]() -> Result { + TRY_VALUE(rg, try_value(12, 10)); + return Ok{rg}; + }(); + TEST_ASSERT_TRUE(test_good.IsOk()); + + auto test_bad = [&]() -> Result { + TRY_VALUE(rg, try_value(10, 12)); + return Ok{rg}; + }(); + TEST_ASSERT_TRUE(test_bad.IsErr()); + + auto test_good_res = [&]() -> Result { + TRY_RESULT(try_value(42, 5)); + return Ok{G{}}; + }(); + TEST_ASSERT_TRUE(test_good_res.IsOk()); + + auto test_bad_res = [&]() -> Result { + TRY_RESULT(try_value(1, 105)); + return Ok{G{}}; + }(); + TEST_ASSERT_TRUE(test_bad_res.IsErr()); +} + +} // namespace test::result_h_ + +AE_TEST_INLINE { + TEST(test::result_h_::test_Result); + TEST(test::result_h_::test_Monadic); + TEST(test::result_h_::test_Macros); +} + +#endif +#endif // AETHER_TYPES_RESULT_H_ From e1ef8d6677326d3cc36c811773aaf4084e1fb97a Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Fri, 27 Feb 2026 14:58:51 +0500 Subject: [PATCH 05/11] fix cppcheck issues --- aether/crypto/hydrogen/hydro_async_crypto_provider.cpp | 2 +- aether/poller/poller_types.h | 2 +- aether/transport/system_sockets/sockets/lwip_cb_tcp_socket.h | 2 +- .../transport/system_sockets/sockets/lwip_cb_udp_socket.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aether/crypto/hydrogen/hydro_async_crypto_provider.cpp b/aether/crypto/hydrogen/hydro_async_crypto_provider.cpp index 7788f4f1..d0de0b30 100644 --- a/aether/crypto/hydrogen/hydro_async_crypto_provider.cpp +++ b/aether/crypto/hydrogen/hydro_async_crypto_provider.cpp @@ -59,7 +59,7 @@ inline std::vector DecryptWithAsymmetric( assert(encrypted_data.size() > hydro_kx_N_PACKET1BYTES + sizeof(std::uint64_t)); - hydro_kx_keypair kp; + hydro_kx_keypair kp{}; std::copy(pk.key.begin(), pk.key.end(), std::begin(kp.pk)); std::copy(secret_key.key.begin(), secret_key.key.end(), std::begin(kp.sk)); diff --git a/aether/poller/poller_types.h b/aether/poller/poller_types.h index d419e3e6..7a299572 100644 --- a/aether/poller/poller_types.h +++ b/aether/poller/poller_types.h @@ -39,7 +39,7 @@ struct EventType { explicit operator std::uint8_t() const { return value; } - std::uint8_t value; + std::uint8_t value{}; }; struct DescriptorType { diff --git a/aether/transport/system_sockets/sockets/lwip_cb_tcp_socket.h b/aether/transport/system_sockets/sockets/lwip_cb_tcp_socket.h index 274e4215..be6511a8 100644 --- a/aether/transport/system_sockets/sockets/lwip_cb_tcp_socket.h +++ b/aether/transport/system_sockets/sockets/lwip_cb_tcp_socket.h @@ -65,7 +65,7 @@ class LwipCBTcpSocket : public ISocket { ConnectedCb connected_cb_; tcp_pcb* pcb_{nullptr}; - ConnectionState connection_state_; + ConnectionState connection_state_{}; DataBuffer recv_buffer_; }; } // namespace ae diff --git a/aether/transport/system_sockets/sockets/lwip_cb_udp_socket.cpp b/aether/transport/system_sockets/sockets/lwip_cb_udp_socket.cpp index edd35d3a..2ed2fb31 100644 --- a/aether/transport/system_sockets/sockets/lwip_cb_udp_socket.cpp +++ b/aether/transport/system_sockets/sockets/lwip_cb_udp_socket.cpp @@ -44,8 +44,8 @@ ISocket& LwipCBUdpSocket::Error(ErrorCb error_cb) { } std::optional LwipCBUdpSocket::Send(Span data) { - pbuf* p; - err_t err; + pbuf* p{}; + err_t err{}; assert(pcb_ != nullptr && "Not connected to the server"); From 58422670df46b40f26afe20dbe158c9cd492141f Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Fri, 27 Feb 2026 14:58:58 +0500 Subject: [PATCH 06/11] fix build for windows --- aether/aether_app.cpp | 3 ++- aether/serial_ports/at_support/at_buffer.cpp | 11 +++++------ .../stream_api/safe_stream/receiving_chunk_list.cpp | 9 +++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/aether/aether_app.cpp b/aether/aether_app.cpp index 56b117a1..7b5777a6 100644 --- a/aether/aether_app.cpp +++ b/aether/aether_app.cpp @@ -219,8 +219,9 @@ static DnsResolver::ptr DnsResolverFactory(AetherAppContext const& context) { .with_id(GlobalId::kDnsResolver) .with_flags(ObjFlags::kUnloadedByDefault), context.aether()); + #else + return {}; # endif - return dns_resolver; # else return DnsResolver::ptr::Create( CreateWith{context.domain()} diff --git a/aether/serial_ports/at_support/at_buffer.cpp b/aether/serial_ports/at_support/at_buffer.cpp index 6c9e2a85..92b4509a 100644 --- a/aether/serial_ports/at_support/at_buffer.cpp +++ b/aether/serial_ports/at_support/at_buffer.cpp @@ -53,7 +53,7 @@ DataBuffer AtBuffer::GetCrate(std::size_t size, std::size_t offset, iterator start) { DataBuffer res; auto copy_offset = offset; - auto remaining_size = size; + auto remaining_size = static_cast(size); for (auto it = start; it != std::end(data_lines_) && res.size() < size; it++) { if (copy_offset >= it->size()) { @@ -61,13 +61,12 @@ DataBuffer AtBuffer::GetCrate(std::size_t size, std::size_t offset, continue; } auto first = it->begin() + static_cast(copy_offset); - auto last = first + static_cast(remaining_size); - if (last > it->end()) { - last = it->end(); - } + auto bucket_size = it->end() - first; + auto last = + first + ((remaining_size < bucket_size) ? remaining_size : bucket_size); res.insert(std::end(res), first, last); - remaining_size -= static_cast(last - first); + remaining_size -= last - first; copy_offset = 0; } diff --git a/aether/stream_api/safe_stream/receiving_chunk_list.cpp b/aether/stream_api/safe_stream/receiving_chunk_list.cpp index 88b36acd..79cca1cc 100644 --- a/aether/stream_api/safe_stream/receiving_chunk_list.cpp +++ b/aether/stream_api/safe_stream/receiving_chunk_list.cpp @@ -95,7 +95,7 @@ void ReceiveChunkList::Acknowledge(SSRingIndex from, SSRingIndex to) { return true; } if (range.IsBefore(to + 1)) { - c.data.clear(); + return true; } return false; }), @@ -166,9 +166,10 @@ void ReceiveChunkList::FixChunkOverlapsEnd(ReceivingChunk& overlapped, SSRingIndex expected_end) { auto const distance = static_cast( expected_end.Distance(overlapped.offset_range().right)); - overlapped.end = ((overlapped.end - overlapped.begin) > distance) - ? overlapped.end - distance - : overlapped.begin; + auto overlapped_size = (overlapped.end - overlapped.begin); + // shift overlapped.end left + overlapped.end = (overlapped_size > distance) ? overlapped.end - distance + : overlapped.begin; } void ReceiveChunkList::MergeChunkProperties(ReceivingChunk& chunk, From 95fde29c5ed734c2de316fe60ad0eafc5f2a14c7 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Fri, 27 Feb 2026 16:39:50 +0500 Subject: [PATCH 07/11] experiment with parallel --- .github/workflows/ci-cd-multi-platforms.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd-multi-platforms.yml b/.github/workflows/ci-cd-multi-platforms.yml index e43fd98c..ec9a8271 100644 --- a/.github/workflows/ci-cd-multi-platforms.yml +++ b/.github/workflows/ci-cd-multi-platforms.yml @@ -118,12 +118,12 @@ jobs: - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build build --config Release --parallel + run: cmake --build build --config Release - name: Test # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --test-dir build --build-config Release --output-on-failure + run: ctest --test-dir build --build-config Release --output-on-failure --parallel - name: Upload uses: actions/upload-artifact@v4 From 5e21ac5559fe61f17c5de92e2e2905f84028f031 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Fri, 27 Feb 2026 16:50:38 +0500 Subject: [PATCH 08/11] add build tests --- .github/workflows/ci-cd-multi-platforms.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-cd-multi-platforms.yml b/.github/workflows/ci-cd-multi-platforms.yml index ec9a8271..70dce98d 100644 --- a/.github/workflows/ci-cd-multi-platforms.yml +++ b/.github/workflows/ci-cd-multi-platforms.yml @@ -113,12 +113,13 @@ jobs: -DCMAKE_C_COMPILER=${{ matrix.config.cc }} -DCMAKE_BUILD_TYPE=Release -DUSER_CONFIG="${{ matrix.platforms.user_config }}" + -DAE_BUILD_TESTS=On -C "${{ matrix.config.initial_cache }}" -S projects/cmake - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build build --config Release + run: cmake --build build --config Release --parallel - name: Test # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). From fd8eff3ba50e9b6d4857b5af4970ae7ceffaf54c Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Mon, 2 Mar 2026 15:34:30 +0500 Subject: [PATCH 09/11] move includes from aether.h and use forward declarations --- aether/access_points/wifi_access_point.cpp | 1 + aether/adapters/wifi_adapter.cpp | 2 + aether/ae_actions/select_client.cpp | 1 + aether/ae_actions/select_client.h | 11 +-- aether/ae_actions/telemetry.cpp | 5 +- aether/aether.cpp | 19 ++++- aether/aether.h | 69 ++++++++++--------- aether/aether_app.cpp | 22 +++--- aether/aether_app.h | 2 + aether/channels/ethernet_channel.cpp | 2 + aether/channels/wifi_channel.cpp | 2 + aether/client.cpp | 2 + aether/client.h | 16 +---- aether/common.h | 1 - aether/config.h | 2 +- aether/dns/dns_c_ares.cpp | 1 + aether/registration/registration.cpp | 3 +- .../system_sockets/sockets/win_udp_socket.h | 2 +- aether/uap/uap.cpp | 3 + tools/registrator/registrator_action.cpp | 2 + 20 files changed, 99 insertions(+), 69 deletions(-) diff --git a/aether/access_points/wifi_access_point.cpp b/aether/access_points/wifi_access_point.cpp index 72975209..dc3f9cd9 100644 --- a/aether/access_points/wifi_access_point.cpp +++ b/aether/access_points/wifi_access_point.cpp @@ -20,6 +20,7 @@ # include # include "aether/aether.h" +# include "aether/server.h" # include "aether/poller/poller.h" # include "aether/dns/dns_resolve.h" # include "aether/adapters/wifi_adapter.h" diff --git a/aether/adapters/wifi_adapter.cpp b/aether/adapters/wifi_adapter.cpp index 51a040b2..ba73b136 100644 --- a/aether/adapters/wifi_adapter.cpp +++ b/aether/adapters/wifi_adapter.cpp @@ -26,6 +26,8 @@ # include "aether/dns/dns_resolve.h" # include "aether/wifi/wifi_driver_factory.h" +# include "aether/tele/tele.h" + namespace ae { # if defined AE_DISTILLATION WifiAdapter::WifiAdapter(ObjProp prop, ObjPtr aether, diff --git a/aether/ae_actions/select_client.cpp b/aether/ae_actions/select_client.cpp index a3680ed8..591adcd3 100644 --- a/aether/ae_actions/select_client.cpp +++ b/aether/ae_actions/select_client.cpp @@ -19,6 +19,7 @@ #include #include "aether/aether.h" +#include "aether/client.h" #include "aether/registration/registration.h" #include "aether/ae_actions/ae_actions_tele.h" diff --git a/aether/ae_actions/select_client.h b/aether/ae_actions/select_client.h index af406482..07138637 100644 --- a/aether/ae_actions/select_client.h +++ b/aether/ae_actions/select_client.h @@ -20,14 +20,15 @@ #include #include "aether/config.h" -#include "aether/client.h" -#include "aether/ptr/ptr_view.h" +#include "aether/obj/obj_ptr.h" #include "aether/actions/action.h" +#include "aether/actions/action_ptr.h" #include "aether/types/state_machine.h" #include "aether/events/event_subscription.h" namespace ae { class Aether; +class Client; class Registration; class SelectClientAction final : public Action { @@ -43,7 +44,7 @@ class SelectClientAction final : public Action { /** * \brief Create with client already ready. */ - SelectClientAction(ActionContext action_context, Client::ptr client); + SelectClientAction(ActionContext action_context, ObjPtr client); #if AE_SUPPORT_REGISTRATION /** @@ -61,11 +62,11 @@ class SelectClientAction final : public Action { UpdateStatus Update(); - Client::ptr const& client() const; + ObjPtr const& client() const; State state() const; private: - Client::ptr client_; + ObjPtr client_; StateMachine state_; #if AE_SUPPORT_REGISTRATION diff --git a/aether/ae_actions/telemetry.cpp b/aether/ae_actions/telemetry.cpp index 39b47866..49752c23 100644 --- a/aether/ae_actions/telemetry.cpp +++ b/aether/ae_actions/telemetry.cpp @@ -21,6 +21,7 @@ # include "aether/aether.h" # include "aether/format/format.h" # include "aether/tele/traps/statistics_trap.h" +# include "aether/tele/traps/tele_statistics.h" # include "aether/mstream.h" # include "aether/mstream_buffers.h" @@ -111,7 +112,9 @@ std::optional Telemetry::CollectTelemetry( return std::nullopt; } auto& statistics_storage = - aether_ptr->tele_statistics->trap()->statistics_store; + tele::TeleStatistics::ptr{aether_ptr->tele_statistics} + ->trap() + ->statistics_store; auto& env_storage = statistics_storage.env_store(); Telemetric res{}; res.cpp.utm_id = env_storage.utm_id; diff --git a/aether/aether.cpp b/aether/aether.cpp index 71605ffb..9d499dd8 100644 --- a/aether/aether.cpp +++ b/aether/aether.cpp @@ -18,16 +18,27 @@ #include -#include "aether/global_ids.h" #include "aether/obj/obj_ptr.h" +#include "aether/ae_actions/time_sync.h" +#include "aether/actions/action_processor.h" + +#include "aether/client.h" +#include "aether/server.h" +#include "aether/registration_cloud.h" #include "aether/work_cloud.h" +#include "aether/registration/registration.h" #include "aether/aether_tele.h" namespace ae { -Aether::Aether(ObjProp prop) : Obj{prop} { AE_TELE_DEBUG(AetherCreated); } +Aether::Aether() : action_processor{make_unique()} {} + +Aether::Aether(ObjProp prop) + : Obj{prop}, action_processor{make_unique()} { + AE_TELE_DEBUG(AetherCreated); +} Aether::~Aether() { AE_TELE_DEBUG(AetherDestroyed); } @@ -35,6 +46,10 @@ void Aether::Update(TimePoint current_time) { update_time = action_processor->Update(current_time); } +Aether::operator ActionContext() const { + return ActionContext{*action_processor}; +} + Client::ptr Aether::CreateClient(ClientConfig const& config, std::string const& client_id) { auto client = FindClient(client_id); diff --git a/aether/aether.h b/aether/aether.h index 6e886641..8f8fd94d 100644 --- a/aether/aether.h +++ b/aether/aether.h @@ -25,27 +25,33 @@ #include "aether/obj/obj.h" #include "aether/actions/action_ptr.h" #include "aether/types/client_config.h" -#include "aether/ae_actions/time_sync.h" + #include "aether/actions/action_context.h" -#include "aether/actions/action_processor.h" #include "aether/ae_actions/select_client.h" -#include "aether/tele/traps/tele_statistics.h" -#include "aether/registration/registration.h" // IWYU pragma: keep -#include "aether/client.h" -#include "aether/crypto.h" -#include "aether/server.h" #include "aether/uap/uap.h" -#include "aether/poller/poller.h" -#include "aether/dns/dns_resolve.h" -#include "aether/adapter_registry.h" -#include "aether/registration_cloud.h" namespace ae { +class Server; +class Client; +class Crypto; +class IPoller; +class DnsResolver; +class Registration; +class TimeSyncAction; +class AdapterRegistry; +class ActionProcessor; +class RegistrationCloud; +class RegistrationCloud; + +namespace tele { +class TeleStatistics; +} + class Aether : public Obj { AE_OBJECT(Aether, Obj, 0) - Aether() = default; + Aether(); public: // Internal. @@ -75,40 +81,39 @@ class Aether : public Obj { void Update(TimePoint current_time) override; // User-facing API. - operator ActionContext() const { return ActionContext{*action_processor}; } + operator ActionContext() const; - Client::ptr CreateClient(ClientConfig const& config, - std::string const& client_id); + ObjPtr CreateClient(ClientConfig const& config, + std::string const& client_id); ActionPtr SelectClient(Uid parent_uid, std::string const& client_id); - void StoreServer(Server::ptr s); - Server::ptr GetServer(ServerId server_id); + void StoreServer(ObjPtr s); + ObjPtr GetServer(ServerId server_id); - Client::ptr client_prefab; - RegistrationCloud::ptr registration_cloud; + Obj::ptr client_prefab; + Obj::ptr registration_cloud; - Crypto::ptr crypto; - IPoller::ptr poller; - DnsResolver::ptr dns_resolver; + Obj::ptr crypto; + Obj::ptr poller; + Obj::ptr dns_resolver; - AdapterRegistry::ptr adapter_registry; + Obj::ptr adapter_registry; Uap::ptr uap; - tele::TeleStatistics::ptr tele_statistics; + Obj::ptr tele_statistics; - std::unique_ptr action_processor = - make_unique(); + std::unique_ptr action_processor; private: - Client::ptr FindClient(std::string const& client_id); - void StoreClient(Client::ptr client); + ObjPtr FindClient(std::string const& client_id); + void StoreClient(ObjPtr client); ActionPtr FindSelectClientAction( std::string const& client_id); ActionPtr MakeSelectClient() const; ActionPtr MakeSelectClient( - Client::ptr const& client) const; + ObjPtr const& client) const; #if AE_SUPPORT_REGISTRATION ActionPtr MakeSelectClient( ActionPtr registration, std::string const& client_id); @@ -119,10 +124,10 @@ class Aether : public Obj { private: #endif - void MakeTimeSyncAction(Client::ptr const& client); + void MakeTimeSyncAction(ObjPtr const& client); - std::map clients_; - std::map servers_; + std::map clients_; + std::map servers_; std::map> select_client_actions_; #if AE_TIME_SYNC_ENABLED diff --git a/aether/aether_app.cpp b/aether/aether_app.cpp index 7b5777a6..f22eeae4 100644 --- a/aether/aether_app.cpp +++ b/aether/aether_app.cpp @@ -24,6 +24,7 @@ #include "aether/crypto/key.h" #include "aether/global_ids.h" #include "aether/adapters/ethernet.h" +#include "aether/registration_cloud.h" #include "aether/adapters/wifi_adapter.h" #include "aether/poller/win_poller.h" #include "aether/poller/epoll_poller.h" @@ -82,7 +83,8 @@ static Aether::ptr AetherFactory(AetherAppContext const& context) { static tele::TeleStatistics::ptr TeleStatisticsFactory( AetherAppContext const& context) { - auto tele_statistics = context.aether()->tele_statistics; + auto tele_statistics = + tele::TeleStatistics::ptr{context.aether()->tele_statistics}; if (tele_statistics.is_valid()) { return tele_statistics; } @@ -98,7 +100,7 @@ static tele::TeleStatistics::ptr TeleStatisticsFactory( #if AE_DISTILLATION static AdapterRegistry::ptr AdapterRegistryFactory( AetherAppContext const& context) { - auto ap = context.aether()->adapter_registry; + auto ap = AdapterRegistry::ptr{context.aether()->adapter_registry}; if (ap.is_valid()) { return ap; } @@ -126,11 +128,11 @@ static Adapter::ptr DefaultAdapterFactory(AetherAppContext const& context) { # if AE_SUPPORT_REGISTRATION static Cloud::ptr RegistrationCloudFactory(AetherAppContext const& context) { - auto reg_c = context.aether()->registration_cloud; - if (reg_c.is_valid()) { - return reg_c; + auto cloud = Cloud::ptr{context.aether()->registration_cloud}; + if (cloud.is_valid()) { + return cloud; } - reg_c = RegistrationCloud::ptr::Create( + auto reg_c = RegistrationCloud::ptr::Create( CreateWith{context.domain()} .with_id(GlobalId::kRegistrationCloud) .with_flags(ObjFlags::kUnloadedByDefault), @@ -151,7 +153,7 @@ static Cloud::ptr RegistrationCloudFactory(AetherAppContext const& context) { # endif // AE_SUPPORT_REGISTRATION static Crypto::ptr CryptoFactory(AetherAppContext const& context) { - auto crypto = context.aether()->crypto; + auto crypto = Crypto::ptr{context.aether()->crypto}; if (crypto.is_valid()) { return crypto; } @@ -172,7 +174,7 @@ static Crypto::ptr CryptoFactory(AetherAppContext const& context) { } static IPoller::ptr PollerFactory(AetherAppContext const& context) { - auto poller = context.aether()->poller; + auto poller = IPoller::ptr{context.aether()->poller}; if (poller.is_valid()) { return poller; } @@ -202,7 +204,7 @@ static IPoller::ptr PollerFactory(AetherAppContext const& context) { } static DnsResolver::ptr DnsResolverFactory(AetherAppContext const& context) { - auto dns_resolver = context.aether()->dns_resolver; + auto dns_resolver = DnsResolver::ptr{context.aether()->dns_resolver}; if (dns_resolver.is_valid()) { return dns_resolver; } @@ -219,7 +221,7 @@ static DnsResolver::ptr DnsResolverFactory(AetherAppContext const& context) { .with_id(GlobalId::kDnsResolver) .with_flags(ObjFlags::kUnloadedByDefault), context.aether()); - #else +# else return {}; # endif # else diff --git a/aether/aether_app.h b/aether/aether_app.h index 740c99a8..29d2e078 100644 --- a/aether/aether_app.h +++ b/aether/aether_app.h @@ -38,10 +38,12 @@ #include "aether/cloud.h" #include "aether/aether.h" #include "aether/crypto.h" +#include "aether/client.h" #include "aether/uap/uap.h" #include "aether/poller/poller.h" #include "aether/dns/dns_resolve.h" #include "aether/adapter_registry.h" +#include "aether/tele/traps/tele_statistics.h" #include "aether/obj/component_factory.h" #include "aether/domain_storage/domain_storage_factory.h" diff --git a/aether/channels/ethernet_channel.cpp b/aether/channels/ethernet_channel.cpp index 09c3e5c6..1ba9f99a 100644 --- a/aether/channels/ethernet_channel.cpp +++ b/aether/channels/ethernet_channel.cpp @@ -30,6 +30,8 @@ #include "aether/channels/ethernet_transport_factory.h" +#include "aether/tele/tele.h" + namespace ae { namespace ethernet_access_point_internal { diff --git a/aether/channels/wifi_channel.cpp b/aether/channels/wifi_channel.cpp index cfe72198..9ec0cf0e 100644 --- a/aether/channels/wifi_channel.cpp +++ b/aether/channels/wifi_channel.cpp @@ -32,6 +32,8 @@ # include "aether/channels/ethernet_transport_factory.h" +# include "aether/tele/tele.h" + namespace ae { namespace wifi_channel_internal { class WifiTransportBuilderAction final : public TransportBuilderAction { diff --git a/aether/client.cpp b/aether/client.cpp index 7fdd474a..7327d436 100644 --- a/aether/client.cpp +++ b/aether/client.cpp @@ -18,6 +18,8 @@ #include +#include "aether/ae_actions/telemetry.h" + #include "aether/aether.h" namespace ae { diff --git a/aether/client.h b/aether/client.h index 9d29df1d..afda3200 100644 --- a/aether/client.h +++ b/aether/client.h @@ -27,7 +27,6 @@ #include "aether/types/uid.h" #include "aether/server_keys.h" -#include "aether/ae_actions/telemetry.h" #include "aether/cloud_connections/cloud_server_connections.h" #include "aether/connection_manager/client_cloud_manager.h" #include "aether/client_messages/p2p_message_stream_manager.h" @@ -36,6 +35,7 @@ namespace ae { class Aether; +class Telemetry; class Client : public Obj { AE_OBJECT(Client, Obj, 0) @@ -68,20 +68,6 @@ class Client : public Obj { ephemeral_uid_, master_key_, cloud_, server_keys_, client_cloud_manager_)) - template - void Load(CurrentVersion, Dnv& dnv) { - dnv(base_); - dnv(aether_, uid_, ephemeral_uid_, master_key_, cloud_, server_keys_, - client_cloud_manager_); - } - - template - void Save(CurrentVersion, Dnv& dnv) const { - dnv(base_); - dnv(aether_, uid_, ephemeral_uid_, master_key_, cloud_, server_keys_, - client_cloud_manager_); - } - void SendTelemetry(); private: diff --git a/aether/common.h b/aether/common.h index 1f2be5b5..71557931 100644 --- a/aether/common.h +++ b/aether/common.h @@ -17,7 +17,6 @@ #ifndef AETHER_COMMON_H_ #define AETHER_COMMON_H_ -#include #include #include "aether/config.h" diff --git a/aether/config.h b/aether/config.h index fdbbc88f..208754bf 100644 --- a/aether/config.h +++ b/aether/config.h @@ -111,7 +111,7 @@ * Also choose one of the supported modem implementations. */ #ifndef AE_SUPPORT_MODEMS -# define AE_SUPPORT_MODEMS 1 +# define AE_SUPPORT_MODEMS 0 #endif // Thingy91x modem implementation is enabled. diff --git a/aether/dns/dns_c_ares.cpp b/aether/dns/dns_c_ares.cpp index 95c522cc..2987f206 100644 --- a/aether/dns/dns_c_ares.cpp +++ b/aether/dns/dns_c_ares.cpp @@ -30,6 +30,7 @@ # include "aether/socket_initializer.h" # include "aether/actions/action_ptr.h" # include "aether/actions/action_context.h" +# include "aether/events/multi_subscription.h" # include "aether/dns/dns_tele.h" diff --git a/aether/registration/registration.cpp b/aether/registration/registration.cpp index f8a27c67..9e1081e7 100644 --- a/aether/registration/registration.cpp +++ b/aether/registration/registration.cpp @@ -21,6 +21,7 @@ # include # include "aether/aether.h" +# include "aether/crypto.h" # include "aether/crypto/sign.h" # include "aether/api_protocol/api_context.h" # include "aether/crypto/crypto_definitions.h" @@ -50,7 +51,7 @@ Registration::Registration(ActionContext action_context, Aether& aether, state_{State::kInitConnection}, // TODO: add configuration response_timeout_{std::chrono::seconds(20)}, - sign_pk_{aether.crypto->signs_pk_[kDefaultSignatureMethod]} { + sign_pk_{Crypto::ptr{aether.crypto}->signs_pk_[kDefaultSignatureMethod]} { AE_TELE_INFO(RegisterStarted); // parent uid must not be empty diff --git a/aether/transport/system_sockets/sockets/win_udp_socket.h b/aether/transport/system_sockets/sockets/win_udp_socket.h index 266fc61e..9ca6bee5 100644 --- a/aether/transport/system_sockets/sockets/win_udp_socket.h +++ b/aether/transport/system_sockets/sockets/win_udp_socket.h @@ -20,7 +20,7 @@ #include "aether/config.h" #include "aether/transport/system_sockets/sockets/win_socket.h" // IWYU pragma: keep -#if AE_SUPPORT_UDP && defined WIN_SOCKET_ENABLED +#if AE_SUPPORT_UDP && WIN_SOCKET_ENABLED namespace ae { class WinUdpSocket final : public WinSocket { diff --git a/aether/uap/uap.cpp b/aether/uap/uap.cpp index 1b4836ef..12982dd1 100644 --- a/aether/uap/uap.cpp +++ b/aether/uap/uap.cpp @@ -16,6 +16,9 @@ #include "aether/uap/uap.h" +#include +#include + #include "aether/aether.h" #include "aether/tele/tele.h" diff --git a/tools/registrator/registrator_action.cpp b/tools/registrator/registrator_action.cpp index 98f8259f..889d1816 100644 --- a/tools/registrator/registrator_action.cpp +++ b/tools/registrator/registrator_action.cpp @@ -16,6 +16,8 @@ #include "registrator/registrator_action.h" +#include "aether/registration/registration.h" + namespace ae::reg { RegistratorAction::RegistratorAction( ActionContext action_context, RcPtr const& aether_app, From 9d7a0ba4b01c7c777ce78f3137d66a54a6e46c18 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Mon, 2 Mar 2026 17:55:12 +0500 Subject: [PATCH 10/11] make TypeAt instantiate less templates --- aether/meta/type_list.h | 68 ++++++++++++++++++++++++------ aether/reflect/reflect_impl.h | 11 ++--- tests/test-meta/test-type-list.cpp | 2 + 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/aether/meta/type_list.h b/aether/meta/type_list.h index 377a609f..75ec6ea1 100644 --- a/aether/meta/type_list.h +++ b/aether/meta/type_list.h @@ -18,30 +18,72 @@ #define AETHER_META_TYPE_LIST_H_ #include -#include +#include namespace ae { template -struct TypeList { - TypeList() = default; +struct TypeList {}; + +template +struct TypeListMaker { + using type = TypeList...>; + template - explicit constexpr TypeList(U&&...) {} + explicit constexpr TypeListMaker(U&&...) {} }; template -TypeList(T...) -> TypeList; +TypeListMaker(T&&...) -> TypeListMaker; + +static inline constexpr std::size_t GetTypeAtChunkSize = 10; + +template +static constexpr auto GetTypeAtChunk() { + static_assert(I < GetTypeAtChunkSize); + if constexpr (I == 0) { + return std::type_identity{}; + } else if constexpr (I == 1) { + return std::type_identity{}; + } else if constexpr (I == 2) { + return std::type_identity{}; + } else if constexpr (I == 3) { + return std::type_identity{}; + } else if constexpr (I == 4) { + return std::type_identity{}; + } else if constexpr (I == 5) { + return std::type_identity{}; + } else if constexpr (I == 6) { + return std::type_identity{}; + } else if constexpr (I == 7) { + return std::type_identity{}; + } else if constexpr (I == 8) { + return std::type_identity{}; + } else if constexpr (I == 9) { + return std::type_identity{}; + } +} + +template +static constexpr auto GetTypeAt() { + if constexpr (I < GetTypeAtChunkSize) { + return GetTypeAtChunk(); + } else { + return GetTypeAt(); + } +} template struct TypeAt; -template -struct TypeAt> { - using type = typename TypeAt>::type; -}; - -template -struct TypeAt<0, TypeList> { - using type = T; +template +struct TypeAt> { + using type = typename decltype(GetTypeAt())::type; }; template diff --git a/aether/reflect/reflect_impl.h b/aether/reflect/reflect_impl.h index da4cccb2..a4011612 100644 --- a/aether/reflect/reflect_impl.h +++ b/aether/reflect/reflect_impl.h @@ -248,9 +248,10 @@ struct IsReflectable; \ static_assert(sizeof(SelfType) != 0); \ - constexpr auto fields = ::ae::TypeList{__VA_ARGS__}; \ + auto fields = ::ae::TypeListMaker{__VA_ARGS__}; \ using TypeFieldTypeList = \ - ::ae::JoinedTypeList_t<::ae::TypeList, decltype(fields)>; \ + ::ae::JoinedTypeList_t<::ae::TypeList, \ + typename decltype(fields)::type>; \ using FL = typename ::ae::TypeListToTemplate< \ ::ae::reflect::reflect_internal::FieldList, TypeFieldTypeList>::type; \ return FL{}; \ @@ -264,10 +265,10 @@ struct IsReflectable; \ static_assert(sizeof(SelfType) != 0); \ - constexpr auto fields = \ - ::ae::TypeList{_AE_APPLY_MACRO(AE_MMBR, __VA_ARGS__)}; \ + auto fields = ::ae::TypeListMaker{_AE_APPLY_MACRO(AE_MMBR, __VA_ARGS__)}; \ using TypeFieldTypeList = \ - ::ae::JoinedTypeList_t<::ae::TypeList, decltype(fields)>; \ + ::ae::JoinedTypeList_t<::ae::TypeList, \ + typename decltype(fields)::type>; \ using FL = typename ::ae::TypeListToTemplate< \ ::ae::reflect::reflect_internal::FieldList, TypeFieldTypeList>::type; \ return FL{}; \ diff --git a/tests/test-meta/test-type-list.cpp b/tests/test-meta/test-type-list.cpp index 5b744e44..d8a5fe2f 100644 --- a/tests/test-meta/test-type-list.cpp +++ b/tests/test-meta/test-type-list.cpp @@ -16,6 +16,7 @@ #include +#include #include "aether/meta/type_list.h" #if defined __GNUC__ @@ -112,6 +113,7 @@ void TestNTypesReverse() { void test_Ntypes() { RUN_TEST(TestNTypes<1>); RUN_TEST(TestNTypes<10>); + RUN_TEST(TestNTypes<100>); RUN_TEST(TestNTypes<450>); From 8a19ba634306ad8a0cdc5549740b9f05539b9b54 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Mon, 2 Mar 2026 18:53:18 +0500 Subject: [PATCH 11/11] try 4 parallel --- .github/workflows/ci-cd-multi-platforms.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-cd-multi-platforms.yml b/.github/workflows/ci-cd-multi-platforms.yml index 70dce98d..997253ea 100644 --- a/.github/workflows/ci-cd-multi-platforms.yml +++ b/.github/workflows/ci-cd-multi-platforms.yml @@ -62,6 +62,7 @@ jobs: generator: "Unix Makefiles", cc: "gcc", cxx: "g++", + jobs_count: 4, initial_cache: ".github/workflows/linux_initial_cache.txt", } - { @@ -119,7 +120,7 @@ jobs: - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build build --config Release --parallel + run: cmake --build build --config Release --parallel ${{ matrix.config.jobs_count }} - name: Test # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).