diff --git a/.github/workflows/ci-cd-multi-platforms.yml b/.github/workflows/ci-cd-multi-platforms.yml index e43fd98c..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", } - { @@ -113,17 +114,18 @@ 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 --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). # 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 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/CMakeLists.txt b/aether/CMakeLists.txt index 60adb1a1..baa251df 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") @@ -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/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/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/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 56b117a1..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,8 +221,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/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/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/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/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/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/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/meta/type_list.h b/aether/meta/type_list.h new file mode 100644 index 00000000..75ec6ea1 --- /dev/null +++ b/aether/meta/type_list.h @@ -0,0 +1,147 @@ +/* + * 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_TYPE_LIST_H_ +#define AETHER_META_TYPE_LIST_H_ + +#include +#include + +namespace ae { +template +struct TypeList {}; + +template +struct TypeListMaker { + using type = TypeList...>; + + template + explicit constexpr TypeListMaker(U&&...) {} +}; + +template +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 decltype(GetTypeAt())::type; +}; + +template +using TypeAt_t = typename TypeAt::type; + +template +struct TypeListSize; + +template +struct TypeListSize> { + static constexpr std::size_t value = sizeof...(Ts); +}; + +template +static inline constexpr std::size_t TypeListSize_v = TypeListSize::value; + +/** + * \brief Join two type lists. + */ +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 + */ +template