diff --git a/.clang-tidy b/.clang-tidy index c4373515..d65343dd 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -22,6 +22,7 @@ Checks: - -misc-non-private-member-variables-in-classes - -misc-use-internal-linkage - -misc-use-anonymous-namespace + - -misc-include-cleaner - -cppcoreguidelines-pro-type-vararg - -cppcoreguidelines-non-private-member-variables-in-classes - -cppcoreguidelines-special-member-functions diff --git a/AGENTS.md b/AGENTS.md index b61a5ac8..9050466f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,9 +10,11 @@ - Initialize variables and objects using `{}` to distinguish from function call. - *Exception vector initialization*: In case vector must be created with size use `()` initialization to distinguish from initializer_list constructor. - Use `auto` whenever possible for variables with respect for references `auto&` and pointers `auto*`. -- Check if pointer is null by comparing it to `nullptr` instead of using implicit conversion to `bool`. +- Check if raw pointer is null by comparing it to `nullptr` instead of using implicit conversion to `bool`. Smart pointers could be check with implicit bool conversion. - If a function argument is not used, there are two options. If it's never used, just ommit the argument name. If it's used on some configurations use `[[maybe_unused]]` attribute. - Immediate lambda call pattern should be implemented by using `std::invoke`. +- For each assert add comment like `assert(condition && "Comment")`. +- For *internal* namespaces use `file_name_internal` format. ## Architecture & Object System @@ -90,6 +92,13 @@ Project build configured in `build-clang` (``) with ninja. To build the project go into `/` and run `cmake --build . --parallel` or `ninja`. + ### Clang-tidy + + - Use `build-clang/compile_commands.json` for `clang-tidy`. + - If `build-clang` exists but `compile_commands.json` is missing, report `clang-tidy` skipped because the compile database is missing, not because the build directory is missing. + - Do not claim the build directory is missing when `build-clang` exists. + - `compile_commands.json` may require configuring with `CMAKE_EXPORT_COMPILE_COMMANDS=ON`. + To run tests, go into `` and run `ctest . --progress -j -E "((sodium)|(hydro)|(bcrypt)).*" --output-on-failure`. Or run specific test by name from `/tests/run/`. diff --git a/aether/ae_actions/ping.h b/aether/ae_actions/ping.h index f7665a65..411deec6 100644 --- a/aether/ae_actions/ping.h +++ b/aether/ae_actions/ping.h @@ -27,6 +27,7 @@ # include "aether/ae_context.h" # include "aether/api_protocol/request_id.h" +# include "aether/clock.h" # include "aether/events/event_subscription.h" # include "aether/events/events.h" # include "aether/tasks/details/task_subsctiption.h" diff --git a/aether/aether_app.cpp b/aether/aether_app.cpp index ef3f084a..00ff8031 100644 --- a/aether/aether_app.cpp +++ b/aether/aether_app.cpp @@ -323,11 +323,11 @@ void AetherAppContext::InitComponentContext() { #endif // AE_DISTILLATION } -RcPtr AetherApp::Construct(AetherAppContext context) { +std::unique_ptr AetherApp::Construct(AetherAppContext context) { // init all the components in context context.InitComponentContext(); - auto app = MakeRcPtr(); + auto app = std::unique_ptr{new AetherApp()}; app->aether_ = context.aether(); #if AE_DISTILLATION app->aether_->tele_statistics = context.tele_statistics_.Resolve(context); diff --git a/aether/aether_app.h b/aether/aether_app.h index d6e70f25..9c84f84a 100644 --- a/aether/aether_app.h +++ b/aether/aether_app.h @@ -24,9 +24,9 @@ #include "aether/common.h" #include "aether/config.h" +#include "aether/memory.h" #include "aether/obj/domain.h" #include "aether/ptr/ptr.h" -#include "aether/ptr/rc_ptr.h" #include "aether/actions/action.h" // IWYU pragma: keep #include "aether/events/events.h" // IWYU pragma: keep @@ -145,10 +145,8 @@ class AetherAppContext { * \brief The enter point to the Aethernet application world */ class AetherApp { - friend auto MakeRcPtr() noexcept; - public: - static RcPtr Construct(AetherAppContext context); + static std::unique_ptr Construct(AetherAppContext context); ~AetherApp(); diff --git a/aether/aether_c/aether_capi.cpp b/aether/aether_c/aether_capi.cpp index fd3fbbe0..f5d6ca80 100644 --- a/aether/aether_c/aether_capi.cpp +++ b/aether/aether_c/aether_capi.cpp @@ -21,36 +21,36 @@ #include -#include "aether/aether_c/extern_c.h" #include "aether/aether_c/c_errors.h" +#include "aether/aether_c/extern_c.h" +#include "aether/actions/actions_queue.h" #include "aether/memory.h" -#include "aether/ptr/ptr.h" #include "aether/obj/obj_ptr.h" +#include "aether/ptr/ptr.h" #include "aether/types/data_buffer.h" -#include "aether/actions/actions_queue.h" -#include "aether/client.h" #include "aether/aether_app.h" +#include "aether/client.h" // IWYU pragma: begin_keeps #include "aether/wifi/wifi_driver_types.h" #include "aether/domain_storage/domain_storage_factory.h" +#include "aether/domain_storage/file_system_std_storage.h" #include "aether/domain_storage/ram_domain_storage.h" #include "aether/domain_storage/spifs_domain_storage.h" -#include "aether/domain_storage/file_system_std_storage.h" #include "aether/adapter_registry.h" #include "aether/adapters/ethernet.h" -#include "aether/adapters/wifi_adapter.h" #include "aether/adapters/modem_adapter.h" +#include "aether/adapters/wifi_adapter.h" #include "aether/client_messages/p2p_message_stream.h" #include "aether/client_messages/p2p_message_stream_manager.h" // IWYU pragma: end_keeps -static ae::RcPtr aether_app; +static std::shared_ptr aether_app; struct AetherClient { ClientConfig config{}; @@ -81,7 +81,7 @@ ae::SelectClientAction& SelectClientImpl(AetherClient* client, void* user_data; }; - auto select_context = ae::MakeRcPtr( + auto select_context = std::make_shared( SelectContext{client, config->client_selected_cb, config->message_received_cb, config->user_data}); @@ -310,7 +310,7 @@ int AetherEnd() { default_client.reset(); } int exit_code = aether_app->IsExited() ? aether_app->ExitCode() : 0; - aether_app.Reset(); + aether_app.reset(); return exit_code; } diff --git a/aether/all.h b/aether/all.h index 6309df8f..20f5be39 100644 --- a/aether/all.h +++ b/aether/all.h @@ -41,7 +41,6 @@ #include "aether/obj/registry.h" #include "aether/ptr/ptr.h" #include "aether/ptr/ptr_view.h" -#include "aether/ptr/rc_ptr.h" #include "aether/domain_storage/domain_storage_factory.h" #include "aether/domain_storage/file_system_std_storage.h" diff --git a/aether/cloud_connections/cloud_server_connection.cpp b/aether/cloud_connections/cloud_server_connection.cpp index 422758b3..4cccca6c 100644 --- a/aether/cloud_connections/cloud_server_connection.cpp +++ b/aether/cloud_connections/cloud_server_connection.cpp @@ -33,7 +33,7 @@ void CloudServerConnection::SetPriority(std::size_t priority) { } void CloudServerConnection::Restream() { - if (client_connection_.get() != nullptr) { + if (client_connection_) { client_connection_->Restream(); } } @@ -44,15 +44,15 @@ void CloudServerConnection::SetQuarantine(bool value) { } bool CloudServerConnection::Connect() { - client_connection_.Reset(); + client_connection_.reset(); client_connection_ = connection_factory_->CreateConnection(server()); - return client_connection_.get() != nullptr; + return static_cast(client_connection_); } -void CloudServerConnection::Disconnect() { client_connection_.Reset(); } +void CloudServerConnection::Disconnect() { client_connection_.reset(); } ClientServerConnection* CloudServerConnection::client_connection() { - if (client_connection_.get() != nullptr) { + if (client_connection_) { return client_connection_.get(); } return nullptr; diff --git a/aether/cloud_connections/cloud_server_connection.h b/aether/cloud_connections/cloud_server_connection.h index 338583cc..1cfa4d18 100644 --- a/aether/cloud_connections/cloud_server_connection.h +++ b/aether/cloud_connections/cloud_server_connection.h @@ -17,8 +17,9 @@ #ifndef AETHER_CLOUD_CONNECTIONS_CLOUD_SERVER_CONNECTION_H_ #define AETHER_CLOUD_CONNECTIONS_CLOUD_SERVER_CONNECTION_H_ +#include + #include "aether/ptr/ptr_view.h" -#include "aether/ptr/rc_ptr.h" #include "aether/server_connections/client_server_connection.h" #include "aether/server_connections/iserver_connection_factory.h" @@ -49,7 +50,7 @@ class CloudServerConnection { private: PtrView server_; IServerConnectionFactory* connection_factory_; - RcPtr client_connection_; + std::shared_ptr client_connection_; std::size_t priority_; bool is_quarantined_; }; diff --git a/aether/config.h b/aether/config.h index 21a5605b..f9ef0d3c 100644 --- a/aether/config.h +++ b/aether/config.h @@ -56,7 +56,7 @@ // Maximum size in bytes for a single pending response pool element. #ifndef AE_API_PROTOCOL_PENDING_RESPONSE_MAX_SIZE -# define AE_API_PROTOCOL_PENDING_RESPONSE_MAX_SIZE 2 * sizeof(void*) +# define AE_API_PROTOCOL_PENDING_RESPONSE_MAX_SIZE 4 * sizeof(void*) #endif #ifndef AE_API_PROTOCOL_PENDING_RESPONSE_ALIGN diff --git a/aether/connection_manager/server_connection_manager.cpp b/aether/connection_manager/server_connection_manager.cpp index db1e713d..ff63c88d 100644 --- a/aether/connection_manager/server_connection_manager.cpp +++ b/aether/connection_manager/server_connection_manager.cpp @@ -24,9 +24,8 @@ ServerConnectionManager::ServerConnectionFactory::ServerConnectionFactory( ServerConnectionManager& server_connection_manager) : server_connection_manager_{&server_connection_manager} {} -RcPtr -ServerConnectionManager::ServerConnectionFactory::CreateConnection( - Ptr const& server) { +auto ServerConnectionManager::ServerConnectionFactory::CreateConnection( + Ptr const& server) -> std::shared_ptr { return server_connection_manager_->CreateConnection(server); } @@ -39,8 +38,8 @@ ServerConnectionManager::GetServerConnectionFactory() { return std::make_unique(*this); } -RcPtr ServerConnectionManager::CreateConnection( - Ptr const& server) { +auto ServerConnectionManager::CreateConnection(Ptr const& server) + -> std::shared_ptr { auto in_cache = FindInCache(server->server_id); if (in_cache) { return in_cache; @@ -51,7 +50,7 @@ RcPtr ServerConnectionManager::CreateConnection( assert(client); auto connection = - MakeRcPtr(ae_context_, client, server); + std::make_shared(ae_context_, client, server); // check updates server_update_subs_ += connection->stream_update_event().Subscribe( @@ -62,7 +61,7 @@ RcPtr ServerConnectionManager::CreateConnection( return connection; } -RcPtr ServerConnectionManager::FindInCache( +std::shared_ptr ServerConnectionManager::FindInCache( ServerId server_id) const { auto it = cached_connections_.find(server_id); if (it != cached_connections_.end()) { diff --git a/aether/connection_manager/server_connection_manager.h b/aether/connection_manager/server_connection_manager.h index d4976402..53d8a816 100644 --- a/aether/connection_manager/server_connection_manager.h +++ b/aether/connection_manager/server_connection_manager.h @@ -18,10 +18,10 @@ #define AETHER_CONNECTION_MANAGER_SERVER_CONNECTION_MANAGER_H_ #include +#include -#include "aether/ptr/ptr.h" -#include "aether/ptr/rc_ptr.h" #include "aether/ae_context.h" +#include "aether/ptr/ptr.h" #include "aether/ptr/ptr_view.h" #include "aether/server_connections/client_server_connection.h" #include "aether/server_connections/iserver_connection_factory.h" @@ -35,7 +35,7 @@ class ServerConnectionManager { explicit ServerConnectionFactory( ServerConnectionManager& server_connection_manager); - RcPtr CreateConnection( + std::shared_ptr CreateConnection( Ptr const& server) override; private: @@ -48,16 +48,17 @@ class ServerConnectionManager { std::unique_ptr GetServerConnectionFactory(); - RcPtr CreateConnection(Ptr const& server); + std::shared_ptr CreateConnection( + Ptr const& server); - RcPtr FindInCache(ServerId server_id) const; + std::shared_ptr FindInCache(ServerId server_id) const; private: void ServerUpdate(ServerId server_id); AeContext ae_context_; PtrView client_; - std::map> cached_connections_; + std::map> cached_connections_; MultiSubscription server_update_subs_; }; } // namespace ae diff --git a/aether/domain_storage/file_system_std_storage.cpp b/aether/domain_storage/file_system_std_storage.cpp index bb69443e..3ce2071e 100644 --- a/aether/domain_storage/file_system_std_storage.cpp +++ b/aether/domain_storage/file_system_std_storage.cpp @@ -18,11 +18,11 @@ #if defined AE_FILE_SYSTEM_STD_ENABLED +# include +# include # include # include # include -# include -# include # include # include "aether/domain_storage/domain_storage_tele.h" @@ -38,8 +38,8 @@ class FstreamStorageWriter final : public IDomainStorageWriter { file.close(); AE_TELE_DEBUG(kFileSystemDsObjSaved, "Saved object id={}, class id={}, version={}, size={}", - query.id.ToString(), query.class_id, - static_cast(query.version), written_size); + query.id, query.class_id, static_cast(query.version), + written_size); } void write(void const* data, std::size_t size) override { @@ -77,7 +77,8 @@ FileSystemStdStorage::~FileSystemStdStorage() = default; std::unique_ptr FileSystemStdStorage::Store( DomainQuery const& query) { - auto class_dir = std::filesystem::path("state") / query.id.ToString() / + auto class_dir = std::filesystem::path("state") / + std::to_string(query.id.id()) / std::to_string(query.class_id); std::filesystem::create_directories(class_dir); @@ -94,7 +95,7 @@ ClassList FileSystemStdStorage::Enumerate(const ae::ObjId& obj_id) { auto state_dir = std::filesystem::path{"state"}; auto ec = std::error_code{}; - auto obj_dir = state_dir / obj_id.ToString(); + auto obj_dir = state_dir / std::to_string(obj_id.id()); for (auto const& class_dir : std::filesystem::directory_iterator(obj_dir, ec)) { auto file_name = class_dir.path().filename().string(); @@ -111,7 +112,8 @@ ClassList FileSystemStdStorage::Enumerate(const ae::ObjId& obj_id) { } DomainLoad FileSystemStdStorage::Load(DomainQuery const& query) { - auto object_dir = std::filesystem::path("state") / query.id.ToString(); + auto object_dir = + std::filesystem::path("state") / std::to_string(query.id.id()); auto ec = std::error_code{}; if (!std::filesystem::exists(object_dir, ec)) { return {DomainLoadResult::kEmpty, {}}; @@ -134,16 +136,17 @@ DomainLoad FileSystemStdStorage::Load(DomainQuery const& query) { return DomainLoad{DomainLoadResult::kEmpty, {}}; } - AE_TELE_DEBUG( - kFileSystemDsObjLoaded, "Loaded object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, static_cast(query.version)); + AE_TELE_DEBUG(kFileSystemDsObjLoaded, + "Loaded object id={}, class id={}, version={}", query.id, + query.class_id, static_cast(query.version)); return {DomainLoadResult::kLoaded, std::make_unique(std::move(f))}; } void FileSystemStdStorage::Remove(ae::ObjId const& obj_id) { - auto object_dir = std::filesystem::path("state") / obj_id.ToString(); + auto object_dir = + std::filesystem::path("state") / std::to_string(obj_id.id()); auto ec = std::error_code{}; if (!std::filesystem::exists(object_dir, ec)) { std::filesystem::create_directory(object_dir); @@ -164,8 +167,7 @@ void FileSystemStdStorage::Remove(ae::ObjId const& obj_id) { class_dir.path().string(), ec2.message()); continue; } - AE_TELE_DEBUG(kFileSystemDsObjRemoved, "Object removed {}", - obj_id.ToString()); + AE_TELE_DEBUG(kFileSystemDsObjRemoved, "Object removed {}", obj_id); } if (ec) { AE_TELE_ERROR(kFileSystemDsRemoveObjError, diff --git a/aether/domain_storage/ram_domain_storage.cpp b/aether/domain_storage/ram_domain_storage.cpp index 38e934cf..0c4c7d50 100644 --- a/aether/domain_storage/ram_domain_storage.cpp +++ b/aether/domain_storage/ram_domain_storage.cpp @@ -20,8 +20,8 @@ # include -# include "aether/mstream_buffers.h" # include "aether/domain_storage/domain_storage_tele.h" +# include "aether/mstream_buffers.h" namespace ae { class RamDomainStorageWriter final : public IDomainStorageWriter { @@ -74,8 +74,7 @@ std::unique_ptr RamDomainStorage::Store( ClassList RamDomainStorage::Enumerate(ObjId const& obj_id) { auto obj_map_it = state.find(obj_id); if (obj_map_it == std::end(state)) { - AE_TELE_INFO(kRamDsEnumObjIdNotFound, "Obj not found {}", - obj_id.ToString()); + AE_TELE_INFO(kRamDsEnumObjIdNotFound, "Obj not found {}", obj_id); return {}; } if (!obj_map_it->second) { @@ -87,8 +86,8 @@ ClassList RamDomainStorage::Enumerate(ObjId const& obj_id) { for (auto& [cls, _] : *obj_map_it->second) { classes.emplace_back(cls); } - AE_TELE_DEBUG(kRamDsEnumerated, "Enumerated for obj {} classes {}", - obj_id.ToString(), classes); + AE_TELE_DEBUG(kRamDsEnumerated, "Enumerated for obj {} classes {}", obj_id, + classes); return classes; } @@ -97,8 +96,7 @@ DomainLoad RamDomainStorage::Load(DomainQuery const& query) { if (obj_map_it == std::end(state)) { AE_TELE_INFO(kRamDsLoadObjIdNoFound, "Unable to find object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, - static_cast(query.version)); + query.id, query.class_id, static_cast(query.version)); return {DomainLoadResult::kEmpty, {}}; } if (!obj_map_it->second) { @@ -109,23 +107,21 @@ DomainLoad RamDomainStorage::Load(DomainQuery const& query) { if (class_map_it == std::end(*obj_map_it->second)) { AE_TELE_INFO(kRamDsLoadObjClassIdNotFound, "Unable to find object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, - static_cast(query.version)); + query.id, query.class_id, static_cast(query.version)); return {DomainLoadResult::kEmpty, {}}; } auto version_it = class_map_it->second.find(query.version); if (version_it == std::end(class_map_it->second)) { AE_TELE_INFO(kRamDsLoadObjVersionNotFound, "Unable to find object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, - static_cast(query.version)); + query.id, query.class_id, static_cast(query.version)); return {DomainLoadResult::kEmpty, {}}; } AE_TELE_DEBUG(kRamDsObjLoaded, "Loaded object id={}, class id={}, version={}, size={}", - query.id.ToString(), query.class_id, - static_cast(query.version), version_it->second.size()); + query.id, query.class_id, static_cast(query.version), + version_it->second.size()); return {DomainLoadResult::kLoaded, std::make_unique(version_it->second, *this)}; @@ -139,7 +135,7 @@ void RamDomainStorage::Remove(ObjId const& obj_id) { } obj_map_it->second.reset(); - AE_TELE_DEBUG(kRamDsObjRemoved, "Removed object {}", obj_id.ToString()); + AE_TELE_DEBUG(kRamDsObjRemoved, "Removed object {}", obj_id); } void RamDomainStorage::CleanUp() { state.clear(); } @@ -151,10 +147,9 @@ void RamDomainStorage::SaveData(DomainQuery const& query, ObjectData&& data) { } auto& saved = (*objcect_classes)[query.class_id][query.version]; saved = std::move(data); - AE_TELE_DEBUG(kRamDsObjSaved, - "Saved object id={}, class id={}, version={}, size={}", - query.id.ToString(), query.class_id, - std::to_string(query.version), saved.size()); + AE_TELE_DEBUG( + kRamDsObjSaved, "Saved object id={}, class id={}, version={}, size={}", + query.id, query.class_id, std::to_string(query.version), saved.size()); } } // namespace ae diff --git a/aether/domain_storage/registrar_domain_storage.cpp b/aether/domain_storage/registrar_domain_storage.cpp index 576283b6..377ee9f5 100644 --- a/aether/domain_storage/registrar_domain_storage.cpp +++ b/aether/domain_storage/registrar_domain_storage.cpp @@ -83,7 +83,7 @@ void RegistrarDomainStorage::SaveState() { Format(file, "static constexpr auto class_array_{} = " "std::array{", - obj_id.ToString(), obj_data->size()); + obj_id, obj_data->size()); PrintMapKeysAsData(file, *obj_data); file << "};\n"; } @@ -99,8 +99,7 @@ void RegistrarDomainStorage::SaveState() { Format(file, "static constexpr auto data_array_{}_{}_{} = " "std::array{", - obj_id.ToString(), class_id, static_cast(version), - data.size()); + obj_id, class_id, static_cast(version), data.size()); PrintData(file, data); file << "};\n"; } @@ -113,8 +112,8 @@ void RegistrarDomainStorage::SaveState() { // write object map file << " ae::StaticMap{{\n"; for (auto const& [obj_id, obj_data] : ram_storage.state) { - file << " std::pair{ std::uint32_t{ " << obj_id.ToString() - << " } , ae::Span{ class_array_" << obj_id.ToString() << " }},\n"; + file << " std::pair{ std::uint32_t{ " << obj_id.id() + << " } , ae::Span{ class_array_" << obj_id.id() << " }},\n"; } file << " }},\n"; @@ -128,10 +127,9 @@ void RegistrarDomainStorage::SaveState() { for (auto const& [class_id, class_data] : *obj_data) { for (auto const& [version, _] : class_data) { file << " std::pair{ ae::ObjectPathKey{ "; - Format(file, "{}, {}, {}", obj_id.ToString(), class_id, - static_cast(version)); + Format(file, "{}, {}, {}", obj_id, class_id, static_cast(version)); file << " }, ae::Span{ "; - Format(file, "data_array_{}_{}_{}", obj_id.ToString(), class_id, + Format(file, "data_array_{}_{}_{}", obj_id, class_id, static_cast(version)); file << " }},\n"; } diff --git a/aether/domain_storage/spifs_domain_storage.cpp b/aether/domain_storage/spifs_domain_storage.cpp index 9eee07b5..f98cea17 100644 --- a/aether/domain_storage/spifs_domain_storage.cpp +++ b/aether/domain_storage/spifs_domain_storage.cpp @@ -124,16 +124,15 @@ SpiFsDomainStorage::~SpiFsDomainStorage() { DeInitFs(); } std::unique_ptr SpiFsDomainStorage::Store( DomainQuery const& query) { // open file - auto file_path = Format("{}/{}/{}/{}", kBasePath, query.id.ToString(), - query.class_id, static_cast(query.version)); + auto file_path = Format("{}/{}/{}/{}", kBasePath, query.id, query.class_id, + static_cast(query.version)); return std::make_unique(*this, file_path, query); } ClassList SpiFsDomainStorage::Enumerate(ObjId const& obj_id) { auto obj_it = object_map_.find(obj_id); if (obj_it == std::end(object_map_)) { - AE_TELE_INFO(kSpifsDsEnumObjIdNotFound, "Obj not found {}", - obj_id.ToString()); + AE_TELE_INFO(kSpifsDsEnumObjIdNotFound, "Obj not found {}", obj_id); return {}; } @@ -141,8 +140,8 @@ ClassList SpiFsDomainStorage::Enumerate(ObjId const& obj_id) { for (auto const& [class_id, _] : obj_it->second) { classes.emplace_back(class_id); } - AE_TELE_DEBUG(kSpifsDsEnumerated, "Enumerated for obj {} classes {}", - obj_id.ToString(), classes); + AE_TELE_DEBUG(kSpifsDsEnumerated, "Enumerated for obj {} classes {}", obj_id, + classes); return classes; } @@ -151,8 +150,7 @@ DomainLoad SpiFsDomainStorage::Load(DomainQuery const& query) { if (obj_map_it == std::end(object_map_)) { AE_TELE_INFO(kSpifsDsLoadObjIdNoFound, "Unable to find object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, - static_cast(query.version)); + query.id, query.class_id, static_cast(query.version)); return {DomainLoadResult::kEmpty, {}}; } if (obj_map_it->second.empty()) { @@ -163,31 +161,29 @@ DomainLoad SpiFsDomainStorage::Load(DomainQuery const& query) { if (class_map_it == std::end(obj_map_it->second)) { AE_TELE_INFO(kSpifsDsLoadObjClassIdNotFound, "Unable to find object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, - static_cast(query.version)); + query.id, query.class_id, static_cast(query.version)); return {DomainLoadResult::kEmpty, {}}; } auto version_it = class_map_it->second.find(query.version); if (version_it == std::end(class_map_it->second)) { AE_TELE_INFO(kSpifsDsLoadObjVersionNotFound, "Unable to find object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, - static_cast(query.version)); + query.id, query.class_id, static_cast(query.version)); return {DomainLoadResult::kEmpty, {}}; } // open file - auto file_path = Format("{}/{}/{}/{}", kBasePath, query.id.ToString(), - query.class_id, static_cast(query.version)); + auto file_path = Format("{}/{}/{}/{}", kBasePath, query.id, query.class_id, + static_cast(query.version)); FILE* file = fopen(file_path.c_str(), "r"); if (file == nullptr) { AE_TELED_ERROR("Failed to open file {} for reading.", file_path); return {}; } - AE_TELE_DEBUG( - kSpifsDsObjLoaded, "Loaded object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, static_cast(query.version)); + AE_TELE_DEBUG(kSpifsDsObjLoaded, + "Loaded object id={}, class id={}, version={}", query.id, + query.class_id, static_cast(query.version)); return {DomainLoadResult::kLoaded, std::make_unique(file)}; @@ -203,13 +199,13 @@ void SpiFsDomainStorage::Remove(const ae::ObjId& obj_id) { for (auto& [class_id, class_data] : obj_map_it->second) { for (auto version : class_data) { // remove the file - auto file_path = Format("{}/{}/{}/{}", kBasePath, obj_id.ToString(), - class_id, static_cast(version.first)); + auto file_path = Format("{}/{}/{}/{}", kBasePath, obj_id, class_id, + static_cast(version.first)); unlink(file_path.c_str()); } } obj_map_it->second.clear(); - AE_TELE_DEBUG(kSpifsDsObjRemoved, "Removed object {}", obj_id.ToString()); + AE_TELE_DEBUG(kSpifsDsObjRemoved, "Removed object {}", obj_id); SyncState(); } @@ -218,8 +214,8 @@ void SpiFsDomainStorage::CleanUp() { for (auto const& [class_id, class_data] : obj_map_data) { for (auto version : class_data) { // remove the file - auto file_path = Format("{}/{}/{}/{}", kBasePath, obj_id.ToString(), - class_id, static_cast(version.first)); + auto file_path = Format("{}/{}/{}/{}", kBasePath, obj_id, class_id, + static_cast(version.first)); unlink(file_path.c_str()); } } diff --git a/aether/domain_storage/static_domain_storage.h b/aether/domain_storage/static_domain_storage.h index 43c986c4..013f76c9 100644 --- a/aether/domain_storage/static_domain_storage.h +++ b/aether/domain_storage/static_domain_storage.h @@ -54,11 +54,10 @@ class StaticDomainStorage final : public IDomainStorage { // object_map is defined in FS_INIT auto const classes = static_domain_data_->object_map.find(obj_id.id()); if (classes == std::end(static_domain_data_->object_map)) { - AE_TELED_ERROR("Obj not found {}", obj_id.ToString()); + AE_TELED_ERROR("Obj not found {}", obj_id); return {}; } - AE_TELED_DEBUG("Enumerated for obj {} classes {}", obj_id.ToString(), - classes->second); + AE_TELED_DEBUG("Enumerated for obj {} classes {}", obj_id, classes->second); return ClassList{std::begin(classes->second), std::end(classes->second)}; } @@ -68,13 +67,12 @@ class StaticDomainStorage final : public IDomainStorage { auto const data = static_domain_data_->state_map.find(obj_path); if (data == std::end(static_domain_data_->state_map)) { AE_TELED_ERROR("Unable to find object id={}, class id={}, version={}", - query.id.ToString(), query.class_id, - static_cast(query.version)); + query.id, query.class_id, static_cast(query.version)); return {DomainLoadResult::kEmpty, {}}; } AE_TELED_DEBUG("Loaded object id={}, class id={}, version={}, size={}", - query.id.ToString(), query.class_id, - static_cast(query.version), data->second.size()); + query.id, query.class_id, static_cast(query.version), + data->second.size()); return DomainLoad{ DomainLoadResult::kLoaded, diff --git a/aether/events/event_deleter.cpp b/aether/events/event_deleter.cpp index 19d27c2f..ba82aad8 100644 --- a/aether/events/event_deleter.cpp +++ b/aether/events/event_deleter.cpp @@ -18,7 +18,7 @@ namespace ae { EventHandlerDeleter::EventHandlerDeleter( - RcPtr const& event_handlers, + std::shared_ptr const& event_handlers, EventHandlersList::Index index) : event_handlers_{event_handlers}, index_{index} {} diff --git a/aether/events/event_deleter.h b/aether/events/event_deleter.h index 0dedf691..f799a74e 100644 --- a/aether/events/event_deleter.h +++ b/aether/events/event_deleter.h @@ -17,8 +17,9 @@ #ifndef AETHER_EVENTS_EVENT_DELETER_H_ #define AETHER_EVENTS_EVENT_DELETER_H_ +#include + #include "aether/common.h" -#include "aether/ptr/rc_ptr.h" #include "aether/events/event_list.h" namespace ae { @@ -28,7 +29,7 @@ namespace ae { */ class EventHandlerDeleter { public: - EventHandlerDeleter(RcPtr const& event_handlers, + EventHandlerDeleter(std::shared_ptr const& event_handlers, EventHandlersList::Index index); AE_CLASS_COPY_MOVE(EventHandlerDeleter) @@ -37,7 +38,7 @@ class EventHandlerDeleter { bool alive() const; private: - RcPtrView event_handlers_; + std::weak_ptr event_handlers_; typename EventHandlersList::Index index_; }; diff --git a/aether/events/events.h b/aether/events/events.h index 4922c232..0abe0060 100644 --- a/aether/events/events.h +++ b/aether/events/events.h @@ -17,16 +17,16 @@ #ifndef AETHER_EVENTS_EVENTS_H_ #define AETHER_EVENTS_EVENTS_H_ -#include -#include #include +#include +#include #include +#include -#include "aether/ptr/rc_ptr.h" -#include "aether/events/event_list.h" -#include "aether/events/event_handler.h" -#include "aether/events/event_deleter.h" #include "aether-miscpp/types/small_function.h" +#include "aether/events/event_deleter.h" +#include "aether/events/event_handler.h" +#include "aether/events/event_list.h" #include "aether/events/event_subscription.h" namespace ae { @@ -46,7 +46,7 @@ class Event { using Subscriber = EventSubscriber; using List = EventHandlersList; - explicit Event() : events_list_{MakeRcPtr()} {} + explicit Event() : events_list_{std::make_shared()} {} ~Event() = default; @@ -58,7 +58,8 @@ class Event { * subscriptions. */ void Emit(TArgs... args) { - EmitImpl(events_list_, std::forward(args)...); + std::shared_ptr list_life_extander = events_list_; + EmitImpl(list_life_extander, std::forward(args)...); } /** @@ -77,7 +78,8 @@ class Event { * Separate static emitter is used to handle self destruction while handler * invoking. */ - static void EmitImpl(RcPtr events_list, TArgs&&... args) { + static void EmitImpl(std::shared_ptr const& events_list, + TArgs&&... args) { auto iterate_list = events_list->Iterator(); for (auto it = std::begin(iterate_list); it != std::end(iterate_list); ++it) { @@ -87,7 +89,7 @@ class Event { } } - RcPtr events_list_; + std::shared_ptr events_list_; }; /** diff --git a/aether/mstream.h b/aether/mstream.h index 4a7d6b64..d6b26a5f 100644 --- a/aether/mstream.h +++ b/aether/mstream.h @@ -30,14 +30,14 @@ #define AETHER_MSTREAM_H_ #include -#include #include #include #include +#include +#include #include #include #include -#include #include #include #include @@ -48,7 +48,6 @@ #include "aether-miscpp/reflect/domain_visitor.h" // IWYU pragma: keep #include "aether-miscpp/reflect/reflect.h" -#include "aether/clock.h" #include "aether/types/nullable_type.h" namespace ae { @@ -75,7 +74,7 @@ class omstream : public ostream { void write(const void* data, size_t size) { ob_.write(data, size); } }; -enum class ReadResult { +enum class ReadResult : char { kNo, kYes, }; @@ -527,8 +526,10 @@ imstream& operator>>(imstream& s, std::optional& v) { template omstream& operator<<(omstream& s, std::variant const& v) { - static_assert(sizeof...(Ts) <= std::numeric_limits::max(), - "std::variant mstream serialization supports at most 255 alternatives (indices 0..254; tag 255 is reserved as the valueless sentinel)"); + static_assert( + sizeof...(Ts) <= std::numeric_limits::max(), + "std::variant mstream serialization supports at most 255 alternatives " + "(indices 0..254; tag 255 is reserved as the valueless sentinel)"); if (v.valueless_by_exception()) { auto const tag = std::numeric_limits::max(); s << tag; @@ -542,8 +543,10 @@ omstream& operator<<(omstream& s, std::variant const& v) { template imstream& operator>>(imstream& s, std::variant& v) { - static_assert(sizeof...(Ts) <= std::numeric_limits::max(), - "std::variant mstream serialization supports at most 255 alternatives (indices 0..254; tag 255 is reserved as the valueless sentinel)"); + static_assert( + sizeof...(Ts) <= std::numeric_limits::max(), + "std::variant mstream serialization supports at most 255 alternatives " + "(indices 0..254; tag 255 is reserved as the valueless sentinel)"); std::uint8_t tag{}; s >> tag; if (!data_was_read(s)) { diff --git a/aether/obj/domain.cpp b/aether/obj/domain.cpp index ab064f50..6b3db271 100644 --- a/aether/obj/domain.cpp +++ b/aether/obj/domain.cpp @@ -26,7 +26,7 @@ namespace ae { DomainGraph::DomainGraph(Domain* domain) : domain(domain) { assert(domain); } Ptr DomainGraph::LoadRootImpl(ObjId obj_id) { - if (!obj_id.IsValid()) { + if (!obj_id.is_valid()) { return {}; } // if already loaded @@ -45,7 +45,7 @@ Ptr DomainGraph::LoadRootImpl(ObjId obj_id) { } Ptr DomainGraph::LoadCopyImpl(ObjId ref_id, ObjId copy_id) { - if (!ref_id.IsValid() || !copy_id.IsValid()) { + if (!ref_id.is_valid() || !copy_id.is_valid()) { return {}; } // if already loaded diff --git a/aether/obj/domain.h b/aether/obj/domain.h index ec06cf75..c9422afa 100644 --- a/aether/obj/domain.h +++ b/aether/obj/domain.h @@ -17,24 +17,24 @@ #ifndef AETHER_OBJ_DOMAIN_H_ #define AETHER_OBJ_DOMAIN_H_ +#include +#include #include #include -#include -#include -#include #include +#include #include "aether/clock.h" #include "aether/mstream.h" -#include "aether/ptr/ptr_view.h" -#include "aether-miscpp/reflect/reflect.h" #include "aether-miscpp/reflect/domain_visitor.h" +#include "aether-miscpp/reflect/reflect.h" +#include "aether/ptr/ptr_view.h" +#include "aether/obj/idomain_storage.h" #include "aether/obj/obj_id.h" #include "aether/obj/registry.h" -#include "aether/obj/idomain_storage.h" #include "aether/obj/version_iterator.h" namespace ae { @@ -183,9 +183,7 @@ void DomainGraph::Load(T& obj, ObjId obj_id) { } if constexpr (HasAnyVersionedLoad::value) { - constexpr auto version_bounds = VersionedLoadMinMax::value; - IterateVersions( + version_iterator( obj, [this, obj_id](auto version, auto& obj) { this->LoadVersion(version, obj, obj_id); }); @@ -222,9 +220,7 @@ void DomainGraph::Save(T const& obj, ObjId obj_id) { } if constexpr (HasAnyVersionedSave::value) { - constexpr auto version_bounds = VersionedSaveMinMax::value; - IterateVersions( + version_iterator( obj, [this, obj_id](auto version, auto& obj) { this->SaveVersion(version, obj, obj_id); }); diff --git a/aether/obj/idomain_storage.h b/aether/obj/idomain_storage.h index 9dce73f5..e448cd31 100644 --- a/aether/obj/idomain_storage.h +++ b/aether/obj/idomain_storage.h @@ -17,10 +17,11 @@ #ifndef AETHER_OBJ_IDOMAIN_STORAGE_H_ #define AETHER_OBJ_IDOMAIN_STORAGE_H_ -#include -#include #include +#include +#include +#include "aether/mstream.h" #include "aether/obj/obj_id.h" namespace ae { diff --git a/aether/obj/obj.h b/aether/obj/obj.h index 6b622d24..bf35079b 100644 --- a/aether/obj/obj.h +++ b/aether/obj/obj.h @@ -32,12 +32,12 @@ #include "aether/config.h" #include "aether-miscpp/crc.h" +#include "aether-miscpp/reflect/reflect.h" +#include "aether/obj/domain.h" #include "aether/obj/obj_id.h" #include "aether/obj/obj_ptr.h" -#include "aether/obj/domain.h" -#include "aether/obj/registry.h" #include "aether/obj/registrar.h" // IWYU pragma: export -#include "aether-miscpp/reflect/reflect.h" +#include "aether/obj/registry.h" namespace ae { /** diff --git a/aether/obj/obj_id.cpp b/aether/obj/obj_id.cpp index b2ddc6a3..7f07948c 100644 --- a/aether/obj/obj_id.cpp +++ b/aether/obj/obj_id.cpp @@ -16,8 +16,8 @@ #include "aether/obj/obj_id.h" -#include #include +#include namespace ae { ObjId ObjId::GenerateUnique() { diff --git a/aether/obj/obj_id.h b/aether/obj/obj_id.h index 40f8cdcf..a7c0923f 100644 --- a/aether/obj/obj_id.h +++ b/aether/obj/obj_id.h @@ -18,9 +18,9 @@ #define AETHER_OBJ_OBJ_ID_H_ #include -#include -#include "aether/mstream.h" +#include "aether-miscpp/format/format.h" +#include "aether-miscpp/reflect/reflect.h" namespace ae { @@ -30,64 +30,80 @@ class ObjId { static ObjId GenerateUnique(); - ObjId() { Invalidate(); } + constexpr ObjId() noexcept = default; + // NOLINTNEXTLINE(*explicit*) + constexpr ObjId(Type i) noexcept : id_{i} {} - constexpr ObjId(Type i) : id_{i} {} + AE_REFLECT_MEMBERS(id_) - constexpr Type id() const { return id_; } + constexpr Type id() const noexcept { return id_; } + constexpr bool is_valid() const noexcept { return id_ != 0; } - void Invalidate() { id_ = 0; } - constexpr bool IsValid() const { return id_ != 0; } - constexpr bool operator<(const ObjId& i) const { return id_ < i.id_; } - constexpr bool operator!=(const ObjId& i) const { return id_ != i.id_; } - constexpr bool operator==(const ObjId& i) const { return id_ == i.id_; } + constexpr bool operator<(ObjId const& i) const noexcept { + return id_ < i.id_; + } + constexpr bool operator!=(ObjId const& i) const noexcept { + return id_ != i.id_; + } + constexpr bool operator==(ObjId const& i) const noexcept { + return id_ == i.id_; + } - ObjId& operator+=(Type i) { + constexpr ObjId& operator+=(Type i) noexcept { id_ += i; return *this; } - constexpr ObjId operator+(Type i) const { return ObjId{id_ + i}; } - - template - friend omstream& operator<<(omstream& s, const ObjId& i) { - return s << i.id_; - } - template - friend imstream& operator>>(imstream& s, ObjId& i) { - return s >> i.id_; - } + constexpr ObjId operator+(Type i) const noexcept { return ObjId{id_ + i}; } - std::string ToString() const { return std::to_string(id_); } + private: + Type id_{0}; +}; - protected: - Type id_; +template <> +struct Formatter : public Formatter { + template + void Format(ObjId value, FormatContext& ctx) const { + Formatter::Format(value.id(), ctx); + } }; class ObjFlags { public: - enum { - // The object is not loaded with deserialization. Load method must be - // used for loading. - kUnloadedByDefault = 1, - kUnloaded = 2, - }; + using Type = std::uint8_t; + // defines for flag values + // none value + static constexpr Type kNone = 0x0; + // The object is not loaded with deserialization. Load method must be + // used for loading. + static constexpr Type kUnloadedByDefault = 0x1; + // current state the object is unloaded + static constexpr Type kUnloaded = 0x2; + + constexpr ObjFlags() noexcept = default; + // NOLINTNEXTLINE(*explicit*) + constexpr ObjFlags(Type v) noexcept : value_(v) {} + + AE_REFLECT_MEMBERS(value_) + + ObjFlags& operator=(Type v) noexcept { + value_ = v; + return *this; + } - ObjFlags(uint8_t v) : value_(v) {} - ObjFlags() = default; + // NOLINTNEXTLINE(*explicit*) + constexpr operator Type() const noexcept { return value_; } - operator std::uint8_t&() { return value_; } + private: + Type value_ = kNone; +}; - template - friend omstream& operator<<(omstream& s, const ObjFlags& i) { - return s << i.value_; - } - template - friend imstream& operator>>(imstream& s, ObjFlags& i) { - return s >> i.value_; +template <> +struct Formatter : public Formatter { + template + void Format(ObjFlags value, FormatContext& ctx) const { + Formatter::Format( + static_cast(value), ctx); } - - private: - std::uint8_t value_ = 0; }; } // namespace ae diff --git a/aether/obj/obj_ptr.h b/aether/obj/obj_ptr.h index 396435ab..5a736d9d 100644 --- a/aether/obj/obj_ptr.h +++ b/aether/obj/obj_ptr.h @@ -196,7 +196,7 @@ ObjPtr ObjPtr::MakeFromThis(T* self) { "T Must be an object type"); auto id = self->obj_id; auto* domain = self->domain; - assert(id.IsValid() && (domain != nullptr) && "Object must be in domain"); + assert(id.is_valid() && (domain != nullptr) && "Object must be in domain"); auto ptr = MakePtrFromThis(self); assert(ptr && "Object must be valid"); return ObjPtr(domain, id, {}, std::move(ptr)); @@ -232,7 +232,7 @@ T const& ObjPtr::operator*() const { template bool ObjPtr::is_valid() const { - return id().IsValid(); + return id().is_valid(); } template bool ObjPtr::is_loaded() const { @@ -277,7 +277,7 @@ ObjPtr ObjPtr::Clone() const { template ObjPtr ObjPtr::Clone(ObjId obj_id) const { assert(is_valid() && "Invalid clone ptr"); - assert(obj_id.IsValid() && "Invalid object ID"); + assert(obj_id.is_valid() && "Invalid object ID"); auto ptr = DomainGraph{domain()}.template LoadCopy(id(), obj_id); return ObjPtr{domain(), obj_id, static_cast(flags() & ~ObjFlags::kUnloaded), diff --git a/aether/obj/registrar.h b/aether/obj/registrar.h index 7a06ad10..efe6752c 100644 --- a/aether/obj/registrar.h +++ b/aether/obj/registrar.h @@ -19,10 +19,11 @@ #include +#include "aether-miscpp/reflect/type_index.h" + #include "aether/ptr/ptr.h" #include "aether/obj/domain.h" #include "aether/obj/registry.h" -#include "aether-miscpp/reflect/type_index.h" namespace ae { template @@ -64,12 +65,12 @@ class Registrar { } static void Load(DomainGraph* domain_graph, Ptr& obj, ObjId obj_id) { - auto self_ptr = static_cast(obj.get()); + auto* self_ptr = static_cast(obj.get()); domain_graph->Load(*self_ptr, obj_id); } static void Save(DomainGraph* domain_graph, Ptr const& obj, ObjId id) { - auto self_ptr = static_cast(obj.get()); + auto const* self_ptr = static_cast(obj.get()); domain_graph->Save(*self_ptr, id); } }; diff --git a/aether/obj/registry.cpp b/aether/obj/registry.cpp index 792edc3c..6f4cf980 100644 --- a/aether/obj/registry.cpp +++ b/aether/obj/registry.cpp @@ -29,7 +29,7 @@ Registry& Registry::GetRegistry() { void Registry::RegisterClass(uint32_t cls_id, std::uint32_t base_id, Factory&& factory) { -#ifdef DEBUG +#if DEBUG // Fixme: Commented out to fix the build crash in MinGW /*std::cout << "Registering class " << factory.class_name << " id " << cls_id << " with base " << base_id << std::endl;*/ @@ -100,7 +100,7 @@ Factory* Registry::FindFactory(std::uint32_t base_id) { return &it->second; } -#ifdef DEBUG +#if DEBUG std::string_view Registry::ClassName(std::uint32_t class_id) { if (class_id == crc32::from_literal("Obj").value) { return "ae::Obj"; @@ -114,7 +114,7 @@ std::string_view Registry::ClassName(std::uint32_t class_id) { #endif // DEBUG void Registry::Log() { -#ifdef DEBUG +#if DEBUG for (const auto& c : factories) { AE_TELE_DEBUG(ObjectRegistryLog, "name {}, id {}, base_id {}", c.second.class_name, c.second.cls_id, c.second.base_id); diff --git a/aether/obj/registry.h b/aether/obj/registry.h index f8d2ee7b..c44e654d 100644 --- a/aether/obj/registry.h +++ b/aether/obj/registry.h @@ -40,7 +40,7 @@ struct Factory { CreateFunc create; LoadFunc load; SaveFunc save; -#ifdef DEBUG +#if DEBUG std::string class_name{}; std::uint32_t cls_id{}; std::uint32_t base_id{}; @@ -49,14 +49,14 @@ struct Factory { class Registry { public: - using Relations = std::unordered_map>; - using Factories = std::unordered_map; + using Relations = std::unordered_map>; + using Factories = std::unordered_map; static Registry& GetRegistry(); - void RegisterClass(uint32_t cls_id, std::uint32_t base_id, Factory&& factory); + void RegisterClass(std::uint32_t cls_id, std::uint32_t base_id, Factory&& factory); void Log(); - bool IsExisting(uint32_t class_id); + bool IsExisting(std::uint32_t class_id); int GenerationDistanceInternal(std::uint32_t base_id, std::uint32_t derived_id); @@ -66,9 +66,9 @@ class Registry { // class doesn't exist. int GenerationDistance(std::uint32_t base_id, std::uint32_t derived_id); - Factory* FindFactory(uint32_t base_id); + Factory* FindFactory(std::uint32_t base_id); -#ifdef DEBUG +#if DEBUG std::string_view ClassName(std::uint32_t class_id); #endif // DEBUG diff --git a/aether/obj/version_iterator.h b/aether/obj/version_iterator.h index aee7d940..0cec501a 100644 --- a/aether/obj/version_iterator.h +++ b/aether/obj/version_iterator.h @@ -17,147 +17,127 @@ #ifndef AETHER_OBJ_VERSION_ITERATOR_H_ #define AETHER_OBJ_VERSION_ITERATOR_H_ -#include +#include #include +#include #include -#include -#include "aether/type_traits.h" +#include "aether-miscpp/meta/index_sequence.h" namespace ae { +using VersionValueType = std::uint8_t; + // use max version for compilation time optimization // increase max version count if required -#if defined(MAX_OBJECT_VERSION) -inline constexpr std::uint8_t MAX_VERSION = MAX_OBJECT_VERSION; +#ifdef MAX_OBJECT_VERSION +inline constexpr VersionValueType kMaxVersion = MAX_OBJECT_VERSION; #else -inline constexpr std::uint8_t MAX_VERSION = 24; +inline constexpr VersionValueType kMaxVersion = 24; #endif // Helper version tag for versioned function overloading -template -struct Version; - -template -struct Version> { - static constexpr std::uint8_t value = V; -}; +template + requires(V <= kMaxVersion) +struct Version : public std::integral_constant {}; // Traits for check supported versioned functionality struct Dnv; -template -struct HasVersionedLoad : std::false_type {}; +template +struct VersionLoadTrait { + template + struct Has : std::false_type {}; + template + struct Has().Load( + Version{}, std::declval()))>> : std::true_type {}; -template -struct HasVersionedLoad().Load( - Version{}, std::declval()))>> - : std::true_type {}; + static constexpr bool value = Has::value; +}; -template -struct HasVersionedSave : std::false_type {}; +template +struct VersionSaveTrait { + template + struct Has : std::false_type {}; + template + struct Has().Save( + Version{}, std::declval()))>> : std::true_type {}; -template -struct HasVersionedSave().Save( - Version{}, std::declval()))>> - : std::true_type {}; + static constexpr bool value = Has::value; +}; /** * \brief Calculate min and max version supported by T and tested with * VersionTrait */ template typename VersionTrait> + template typename VersionTrait> struct VersionBounds { - template - static constexpr std::pair CalcVersionBounds( - std::integer_sequence const&) { + template + using i_seq = std::integer_sequence; + + template + static consteval auto CalcVersionBounds(i_seq) + -> std::pair { constexpr auto arr = std::array{VersionTrait::value...}; - std::uint8_t min{}; - std::uint8_t max{}; + VersionValueType min{}; + VersionValueType max{}; std::size_t i = 0; for (; i < arr.size(); ++i) { if (arr[i]) { break; } } - min = static_cast(i); + if (i == arr.size()) { + return {0, 0}; + } + + min = static_cast(i); for (; i < arr.size(); ++i) { if (!arr[i]) { break; } } - max = static_cast(i - 1); + max = static_cast(i - 1); return {min, max}; } static constexpr auto value = CalcVersionBounds( - std::make_integer_sequence()); + std::make_integer_sequence()); }; -/** - * \brief Min and max versions supported by T to Load - */ -template -using VersionedLoadMinMax = VersionBounds; - -/** - * \brief Min and max versions supported by T to Save - */ -template -using VersionedSaveMinMax = VersionBounds; - -template -struct HasAnyVersionedLoad { - template - static constexpr bool TestAny(std::integer_sequence const&) { - return (HasVersionedLoad::value || ...); +template typename VersionTrait> +struct HasAnyVersioned { + template + static consteval bool TestAny( + std::integer_sequence const&) { + return (VersionTrait::value || ...); } - static constexpr auto version_bounds = VersionedLoadMinMax::value; + static constexpr auto version_bounds = VersionBounds::value; static constexpr bool value = TestAny(make_range_sequence()); }; template -struct HasAnyVersionedSave { - template - static constexpr bool TestAny(std::integer_sequence const&) { - return (HasVersionedSave::value || ...); - } - - static constexpr auto version_bounds = VersionedSaveMinMax::value; - static constexpr bool value = - TestAny(make_range_sequence()); -}; - -template