From 9fc875d313c14b6055c79d53143cae97d8486803 Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Mon, 10 Nov 2025 23:50:03 +0530 Subject: [PATCH 01/16] Update CMakeLists.txt --- CMakeLists.txt | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6bc7bb..6f254f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,22 +23,39 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) ) if(COMPILER_SUPPORTS_STD) - message(STATUS " Using C++${standard}") + message(STATUS "Using C++${standard}") break() endif() endforeach() if(NOT COMPILER_SUPPORTS_STD) - message(FATAL_ERROR "❌ No supported C++ standard found! Please use GCC 11 or higher.") + message(FATAL_ERROR "No supported C++ standard found. Please use GCC 11+, Clang 13+, or MSVC 2019+.") endif() endif() # ----------------------------------- -# Build Settings +# Compiler Detection and Flags # ----------------------------------- +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + message(STATUS "Detected Clang compiler") + set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -Wpedantic -Wno-unused-parameter") + set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++ -static-libgcc") + +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + message(STATUS "Detected GCC compiler") + set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -pedantic") + set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") + +elseif(MSVC) + message(STATUS "Detected MSVC compiler") + add_compile_options(/W4 /EHsc) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /std:c++20") + +else() + message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}") +endif() + set(CMAKE_BUILD_TYPE Debug) -set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -pedantic") -set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") # ----------------------------------- # External Dependencies @@ -115,4 +132,4 @@ gtest_discover_tests(TestApp # ----------------------------------- # Output Summary # ----------------------------------- -message(STATUS "✅ Project Configured Successfully.") \ No newline at end of file +message(STATUS "Project configured successfully.") From 0a8931d1668c8ba78d32bcb2fd0947b945efb5be Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Mon, 10 Nov 2025 23:52:07 +0530 Subject: [PATCH 02/16] Update CMakeLists.txt --- CMakeLists.txt | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f254f8..c3de485 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,3 @@ - - cmake_minimum_required(VERSION 3.16) project(ItemManagerProject LANGUAGES CXX) @@ -23,39 +21,22 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) ) if(COMPILER_SUPPORTS_STD) - message(STATUS "Using C++${standard}") + message(STATUS " Using C++${standard}") break() endif() endforeach() if(NOT COMPILER_SUPPORTS_STD) - message(FATAL_ERROR "No supported C++ standard found. Please use GCC 11+, Clang 13+, or MSVC 2019+.") + message(FATAL_ERROR "❌ No supported C++ standard found! Please use GCC 11 or higher.") endif() endif() # ----------------------------------- -# Compiler Detection and Flags +# Build Settings # ----------------------------------- -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - message(STATUS "Detected Clang compiler") - set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -Wpedantic -Wno-unused-parameter") - set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++ -static-libgcc") - -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - message(STATUS "Detected GCC compiler") - set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -pedantic") - set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") - -elseif(MSVC) - message(STATUS "Detected MSVC compiler") - add_compile_options(/W4 /EHsc) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /std:c++20") - -else() - message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}") -endif() - set(CMAKE_BUILD_TYPE Debug) +set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -pedantic") +set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") # ----------------------------------- # External Dependencies @@ -132,4 +113,4 @@ gtest_discover_tests(TestApp # ----------------------------------- # Output Summary # ----------------------------------- -message(STATUS "Project configured successfully.") +message(STATUS "✅ Project Configured Successfully.") From d328ccab3f2b89b341928744045c2dea7a3332e8 Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Mon, 10 Nov 2025 23:54:06 +0530 Subject: [PATCH 03/16] Update CMakeLists.txt added Clang and MSVC Compiler Support --- CMakeLists.txt | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3de485..bbb82dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,22 +21,39 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) ) if(COMPILER_SUPPORTS_STD) - message(STATUS " Using C++${standard}") + message(STATUS "Using C++${standard}") break() endif() endforeach() if(NOT COMPILER_SUPPORTS_STD) - message(FATAL_ERROR "❌ No supported C++ standard found! Please use GCC 11 or higher.") + message(FATAL_ERROR "No supported C++ standard found. Please use GCC 11+, Clang 13+, or MSVC 2019+.") endif() endif() # ----------------------------------- -# Build Settings +# Compiler Detection and Flags # ----------------------------------- +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + message(STATUS "Detected Clang compiler") + set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -Wpedantic -Wno-unused-parameter") + set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++ -static-libgcc") + +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + message(STATUS "Detected GCC compiler") + set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -pedantic") + set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") + +elseif(MSVC) + message(STATUS "Detected MSVC compiler") + add_compile_options(/W4 /EHsc) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /std:c++20") + +else() + message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}") +endif() + set(CMAKE_BUILD_TYPE Debug) -set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -pedantic") -set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") # ----------------------------------- # External Dependencies @@ -113,4 +130,4 @@ gtest_discover_tests(TestApp # ----------------------------------- # Output Summary # ----------------------------------- -message(STATUS "✅ Project Configured Successfully.") +message(STATUS "Project configured successfully.") From 1a4efe68d8e8d8439d5b09cff8c5b8f91ff8e9b6 Mon Sep 17 00:00:00 2001 From: gem870 Date: Tue, 11 Nov 2025 19:44:07 +0100 Subject: [PATCH 04/16] Added features for cross platform build --- CMakeLists.txt | 79 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbb82dd..8e11b97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,25 @@ + + + cmake_minimum_required(VERSION 3.16) project(ItemManagerProject LANGUAGES CXX) # ----------------------------------- -# Compiler and Build Configuration +# Compiler and build configuration # ----------------------------------- # Only run this logic if this is the top-level project if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SUPPORTED_CXX_STANDARDS 20 17 14) + # Ensure a dummy source exists for try_compile checks + set(_dummy_dir "${PROJECT_SOURCE_DIR}/cmake") + set(_dummy_src "${_dummy_dir}/dummy.cpp") + if(NOT EXISTS "${_dummy_src}") + file(MAKE_DIRECTORY "${_dummy_dir}") + file(WRITE "${_dummy_src}" "#include \nint main(){std::cout<<\"ok\";return 0;}\n") + endif() + foreach(standard IN LISTS SUPPORTED_CXX_STANDARDS) set(CMAKE_CXX_STANDARD ${standard}) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -16,7 +27,7 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) try_compile(COMPILER_SUPPORTS_STD ${CMAKE_BINARY_DIR}/std_check - ${PROJECT_SOURCE_DIR}/cmake/dummy.cpp + ${_dummy_src} CMAKE_FLAGS "-DCMAKE_CXX_STANDARD=${standard}" ) @@ -31,32 +42,42 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) endif() endif() +# Default build type for single-config generators (respect multi-config like VS/Xcode) +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose build type (Debug, Release, RelWithDebInfo, MinSizeRel)" FORCE) +endif() + # ----------------------------------- -# Compiler Detection and Flags +# Compiler detection and flags (portable) # ----------------------------------- + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") message(STATUS "Detected Clang compiler") - set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -Wpedantic -Wno-unused-parameter") - set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++ -static-libgcc") + add_compile_options(-g -O1 -Wall -Wextra -Wpedantic -Wno-unused-parameter) + +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + message(STATUS "Detected AppleClang compiler (macOS)") + add_compile_options(-g -O1 -Wall -Wextra -Wpedantic -Wno-unused-parameter) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") message(STATUS "Detected GCC compiler") - set(CMAKE_CXX_FLAGS "-g -O1 -Wall -Wextra -pedantic") - set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") + add_compile_options(-g -O1 -Wall -Wextra -pedantic) elseif(MSVC) message(STATUS "Detected MSVC compiler") - add_compile_options(/W4 /EHsc) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /std:c++20") - + add_compile_options(/W4 /EHsc /Zc:__cplusplus /std:c++20) else() message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}") endif() -set(CMAKE_BUILD_TYPE Debug) +# Optional: allow static libstdc++/libgcc on Linux only (off by default) +option(ENABLE_STATIC_STDLIB "Link libstdc++/libgcc statically on Linux (may affect portability)" OFF) +if(ENABLE_STATIC_STDLIB AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) + add_link_options(-static-libstdc++ -static-libgcc) +endif() # ----------------------------------- -# External Dependencies +# External dependencies # ----------------------------------- include(FetchContent) @@ -69,16 +90,17 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(json) -# GoogleTest +# GoogleTest (do not use system-installed to keep cross-platform consistency) +# GoogleTest (use release tarball instead of git clone) FetchContent_Declare( googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.12.1 + URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip ) FetchContent_MakeAvailable(googletest) + # ----------------------------------- -# ItemManager Library +# ItemManager library # ----------------------------------- add_library(ItemManagerLib STATIC @@ -94,10 +116,13 @@ target_include_directories(ItemManagerLib ${json_SOURCE_DIR}/single_include ) -target_link_libraries(ItemManagerLib PUBLIC nlohmann_json::nlohmann_json) +target_link_libraries(ItemManagerLib + PUBLIC + nlohmann_json::nlohmann_json +) # ----------------------------------- -# Test Executable +# Test executable # ----------------------------------- add_executable(TestApp @@ -111,14 +136,20 @@ target_include_directories(TestApp ${json_SOURCE_DIR}/single_include ) -target_link_libraries(TestApp PRIVATE - ItemManagerLib - gtest - gtest_main +target_link_libraries(TestApp + PRIVATE + ItemManagerLib + gtest + gtest_main ) +# Color output for MSVC GoogleTest (optional quality-of-life) +if(MSVC) + target_compile_definitions(TestApp PRIVATE GTEST_COLOR=1) +endif() + # ----------------------------------- -# Enable Test Discovery +# Enable test discovery # ----------------------------------- include(GoogleTest) @@ -128,6 +159,6 @@ gtest_discover_tests(TestApp ) # ----------------------------------- -# Output Summary +# Output summary # ----------------------------------- message(STATUS "Project configured successfully.") From 303231a2d81391bcb6560726610d522fa2e9228d Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:38:37 +0530 Subject: [PATCH 05/16] Update ItemManager.h --- src/t_manager/ItemManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t_manager/ItemManager.h b/src/t_manager/ItemManager.h index 4eed684..5003cc0 100644 --- a/src/t_manager/ItemManager.h +++ b/src/t_manager/ItemManager.h @@ -15,7 +15,7 @@ #include #include #include -#include +//#include already defined below #include #include #include From 45de6699882192ec62f23518a1f3d59ed6f9e4aa Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:39:53 +0530 Subject: [PATCH 06/16] Update ItemWrapper.h --- src/t_wrapper/ItemWrapper.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/t_wrapper/ItemWrapper.h b/src/t_wrapper/ItemWrapper.h index 12c335b..f219d0b 100644 --- a/src/t_wrapper/ItemWrapper.h +++ b/src/t_wrapper/ItemWrapper.h @@ -16,7 +16,13 @@ #include #include #include -#include +//#include +#ifdef __has_include +# if __has_include() +# include +# endif +#endif + #include #include #include @@ -166,4 +172,4 @@ mutable std::string id_; // Unique ID for each item, mutable to allow modificati #include "ItemWrapper.tpp" // Template definitions should be included at the end of the header -#endif // ITEM_MANAGER_H \ No newline at end of file +#endif // ITEM_MANAGER_H From 5061b8dfd8e1e399013693be4a1b9ef393bb807f Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:49:50 +0530 Subject: [PATCH 07/16] Enhance Windows installation instructions in README Added installation options for Windows with CMake and Visual Studio. --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index e939e23..c427ea4 100644 --- a/README.md +++ b/README.md @@ -130,9 +130,18 @@ Flow from Item Registration to Export ## Installation ### Windows: + Option 1: + . Make sure you have CMake and the Visual Studio 2022(C++) installed before you run the following commands.
+ ```bash + cd Smart_Store + cmake -B build-msvc -G "Visual Studio 17 2022" && cmake --build + build-msvc --config Debug + ``` + Option 2: . Make sure you have CMake and the GCC compiler installed before you run the following commands.
. If still having issues, set up WSL (Windows Subsystem for Linux) or use a Linux environment
and run the following commands. + ### Linux / macOS: . Make sure you have CMake and the GCC compiler installed before you run the following commands.

From 5b04404ca6021dc7bb6c49d3ee8a4d6d67f899c1 Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:58:16 +0530 Subject: [PATCH 08/16] Enhance build instructions for GCC and Clang Updated README to include Clang compiler instructions and modified GCC build commands. --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c427ea4..ce0d324 100644 --- a/README.md +++ b/README.md @@ -144,16 +144,18 @@ Flow from Item Registration to Export ### Linux / macOS: - . Make sure you have CMake and the GCC compiler installed before you run the following commands.

+ . Make sure you have CMake and the GCC and Clang compilers installed before you run the following commands.

-```bash +```bash "GCC" +git clone https://github.com/gem870/Smart_Store.git +cd Smart_Store +cmake -B build-gcc -DCMAKE_CXX_COMPILER=g++ && cmake --build build-gcc +``` +```bash "Clang" git clone https://github.com/gem870/Smart_Store.git cd Smart_Store -mkdir build -cd build -cmake .. -cmake --build . +cmake -B build-clang -DCMAKE_CXX_COMPILER=clang++ && cmake --build build-clang ``` # To output on the console. ### For PowerShell use: From 0b6a5ba3593f9548e4d846628b236096d5bda09d Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:59:45 +0530 Subject: [PATCH 09/16] Fix formatting of bash commands in README Removed unnecessary quotes from bash code blocks in README. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce0d324..e4cc84f 100644 --- a/README.md +++ b/README.md @@ -147,12 +147,14 @@ Flow from Item Registration to Export . Make sure you have CMake and the GCC and Clang compilers installed before you run the following commands.

-```bash "GCC" +```bash +"GCC" git clone https://github.com/gem870/Smart_Store.git cd Smart_Store cmake -B build-gcc -DCMAKE_CXX_COMPILER=g++ && cmake --build build-gcc ``` -```bash "Clang" +```bash +"Clang" git clone https://github.com/gem870/Smart_Store.git cd Smart_Store cmake -B build-clang -DCMAKE_CXX_COMPILER=clang++ && cmake --build build-clang From 54c1a66c227663666dcc455aacdf660069061610 Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Fri, 14 Nov 2025 16:05:13 +0530 Subject: [PATCH 10/16] Update README with TestApp execution steps Added instructions to navigate to the Debug directory and run TestApp. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e4cc84f..889be1f 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,9 @@ Flow from Item Registration to Export cd Smart_Store cmake -B build-msvc -G "Visual Studio 17 2022" && cmake --build build-msvc --config Debug + cd build-msvc + cd Debug + ./TestApp ``` Option 2: . Make sure you have CMake and the GCC compiler installed before you run the following commands.
From 0ec4eb22439f4fcd5447113208a7608742e0e2b5 Mon Sep 17 00:00:00 2001 From: Moin Shaikh Date: Mon, 29 Dec 2025 01:32:04 +0530 Subject: [PATCH 11/16] Save optimized ItemManager and ItemWrapper changes --- src/t_manager/ItemManager.tpp | 476 ++++++++++++++++++++++++++++++++++ src/t_wrapper/ItemWrapper.h | 25 ++ 2 files changed, 501 insertions(+) diff --git a/src/t_manager/ItemManager.tpp b/src/t_manager/ItemManager.tpp index 7ba3331..82b42a7 100644 --- a/src/t_manager/ItemManager.tpp +++ b/src/t_manager/ItemManager.tpp @@ -463,6 +463,10 @@ void ItemManager::asyncExportToFile_Json(const std::string& filename) const { }).detach(); // Fire-and-forget } + + +/* + void ItemManager::importFromFile_Json(const std::string& filename) { if (filename.empty()) { @@ -560,6 +564,110 @@ void ItemManager::importFromFile_Json(const std::string& filename) { LOG_CONTEXT(LogLevel::INFO, "Completed import of " + std::to_string(importCount) + " item(s) from JSON file: " + filename, {}); } +<<<<<<< HEAD + +======= +*/ + +void ItemManager::importFromFile_Json(const std::string& filename) { + + if (filename.empty()) { + LOG_CONTEXT(LogLevel::ERR, "Cannot import from empty filename.", ErrorCode::ITEM_NOT_FOUND); + } + + LOG_CONTEXT(LogLevel::INFO, "Attempting JSON import from file: " + filename, {}); + + std::ifstream in(filename); + if (!in) { + LOG_CONTEXT(LogLevel::ERR, "Cannot open file for reading: " + filename, ErrorCode::FILE_LOAD_FAILED); + } + + json parsedJson; + in >> parsedJson; + + std::cout << Logger::getColorCode(LogColor::CYAN) + "\n:::| Loaded JSON content from file:\n" + << Logger::getColorCode(LogColor::RESET) << parsedJson.dump(2) << "\n"; + LOG_CONTEXT(LogLevel::DEBUG, "JSON file loaded successfully: " + filename, {}); + + if (parsedJson.is_array()) { + LOG_CONTEXT(LogLevel::DEBUG, "Processing JSON array format.", {}); + } else if (parsedJson.contains("items") && parsedJson["items"].is_array()) { + parsedJson = parsedJson["items"]; + LOG_CONTEXT(LogLevel::DEBUG, "Processing JSON with 'items' key.", {}); + } else { + LOG_CONTEXT(LogLevel::ERR, "", std::make_exception_ptr(std::runtime_error( + "Invalid JSON format: " + filename + " Expected an array or 'items' key."))); + } + + undoHistory.push_back(cloneCurrentState()); + redoQueue = {}; + items.clear(); + + int importCount = 0; + + std::unordered_map demangledCache; + std::unordered_map desCache; + + for (const auto& entry : parsedJson) { + // 2. Use find() to get iterators – this is the fastest way to check existence and get data + auto itTag = entry.find("tag"); + auto itType = entry.find("type"); + auto itData = entry.find("data"); + + // Skip if any essential key is missing + if (itTag == entry.end() || itType == entry.end() || itData == entry.end()) continue; + + // 3. Zero-copy string access: get a reference directly from the JSON memory + const std::string& tag = itTag->get_ref(); + const std::string& typeName = itType->get_ref(); + + // 4. Handle Demangling Cache (CPU Optimization) + if (demangledCache.find(typeName) == demangledCache.end()) { + demangledCache[typeName] = demangleType(typeName); + } + const std::string& prettyName = demangledCache[typeName]; + + // 5. Handle Deserializer Cache (Lookup Optimization) + auto cachedDesIt = desCache.find(typeName); + if (cachedDesIt == desCache.end()) { + auto globalIt = deserializers.find(typeName); + desCache[typeName] = (globalIt != deserializers.end()) ? globalIt->second : nullptr; + cachedDesIt = desCache.find(typeName); + } + + if (!cachedDesIt->second) { + LOG_CONTEXT(LogLevel::WARNING, "No deserializer for: " + prettyName, {}); + continue; + } + + // 6. Process Data + int version = entry.value("version", 1); + json rawData = *itData; // Copy once for migration + + if (auto itId = entry.find("id"); itId != entry.end() && !rawData.contains("id")) { + rawData["id"] = *itId; + } + + // 7. Schema Registry: Only store if we haven't seen this type in this session + if (auto itSchema = entry.find("schema"); itSchema != entry.end()) { + schemaRegistry.try_emplace(typeName, [schema = *itSchema]() { return schema; }); + } + + try { + json upgraded = migrationRegistry.upgradeToLatest(typeName, version, rawData); + + // Use the cached function pointer to deserialize + if (auto newItem = cachedDesIt->second(upgraded, tag)) { + items[tag] = std::move(newItem); + ++importCount; + } + } catch (const std::exception& e) { + LOG_CONTEXT(LogLevel::ERR, "Import fail [" + tag + "]: " + e.what(), {}); + } + } + + LOG_CONTEXT(LogLevel::INFO, "Completed import of " + std::to_string(importCount) + " item(s) from JSON file: " + filename, {}); +} void ItemManager::asyncImportFromFile_Json(const std::string& filename) { std::thread([this, filename]() { @@ -597,6 +705,7 @@ std::shared_ptr ItemManager::importSingleObject_Json(const std::string } + for (const auto& entry : array) { if (entry.value("tag", "") == tag && entry.value("type", "") == typeName) { @@ -653,6 +762,71 @@ std::shared_ptr ItemManager::importSingleObject_Json(const std::string LOG_CONTEXT(LogLevel::ERR, "", std::make_exception_ptr(std::runtime_error( "Exception during deserialization of '" + tag + "': " + e.what()))); } + + // Pre-demangle the target type name once before entering the loop + const std::string targetTypeNameDemangled = demangleType(typeName); + + for (const auto& entry : array) { + // 1. Efficient key access using find() to avoid double lookups + auto itTag = entry.find("tag"); + auto itType = entry.find("type"); + + if (itTag == entry.end() || itType == entry.end()) continue; + + // 2. Zero-copy comparison using get_ref to avoid string allocations + if (itTag->get_ref() == tag && + itType->get_ref() == typeName) { + + LOG_CONTEXT(LogLevel::INFO, "Found match: '" + tag + "' [" + targetTypeNameDemangled + "]", {}); + + // 3. Extract version and data pointer + int version = entry.value("version", 1); + auto itData = entry.find("data"); + if (itData == entry.end()) { + LOG_CONTEXT(LogLevel::ERR, "Object found but 'data' key is missing.", {}); + return nullptr; + } + + // Copy rawData once for potential mutation/migration + json rawData = *itData; + + // 4. Inject ID if it exists at the entry level but not inside data + if (auto itId = entry.find("id"); itId != entry.end()) { + if (!rawData.contains("id")) { + rawData["id"] = *itId; + } + } + + // 5. Schema Registration (using try_emplace to avoid redundant lambda creation) + if (auto itSchema = entry.find("schema"); itSchema != entry.end()) { + schemaRegistry.try_emplace(typeName, [s = *itSchema]() { return s; }); + } + + // 6. Migration and Deserialization + try { + json upgraded = migrationRegistry.upgradeToLatest(typeName, version, rawData); + + // Check deserializer once + auto desIt = deserializers.find(typeName); + if (desIt == deserializers.end()) [[unlikely]] { + LOG_CONTEXT(LogLevel::ERR, "No deserializer for type: " + targetTypeNameDemangled, {}); + return nullptr; + } + + auto item = desIt->second(upgraded, tag); + if (item) { + // Successful path + undoHistory.push_back(cloneCurrentState()); + redoQueue = {}; + items[tag] = item; + return item; + } + } catch (const std::exception& e) { + LOG_CONTEXT(LogLevel::ERR, "Deserialization error for '" + tag + "': " + e.what(), {}); + } + + return nullptr; // Stop searching once target is found, even if it failed + } } @@ -691,6 +865,7 @@ bool ItemManager::exportToFile_Binary(const std::string& filename) const { std::vector buffer; + for (const auto& [tag, item] : items) { json serializedJson = item->serialize(); serializedJson["id"] = item->getId(); @@ -734,6 +909,45 @@ bool ItemManager::exportToFile_Binary(const std::string& filename) const { dumpHex(tagStr.data(), tagSize); dumpHex(&dataSize, sizeof(dataSize)); dumpHex(jsonStr.data(), dataSize); + + buffer.reserve(items.size() * 512); + + for (const auto& [tag, item] : items) { + if (!item) [[unlikely]] continue; + + // 2. Serialize and prepare metadata + json serializedJson = item->serialize(); + serializedJson["id"] = item->getId(); + serializedJson["tag"] = tag; + serializedJson["type"] = item->getTypeName(); + + const std::string& typeName = item->getTypeName(); + const std::string jsonStr = serializedJson.dump(); + + uint32_t typeSize = static_cast(typeName.size()); + uint32_t tagSize = static_cast(tag.size()); + uint32_t dataSize = static_cast(jsonStr.size()); + + // 3. High-speed Append logic using std::memcpy + // This avoids the overhead of buffer.insert (which checks capacity every time) + auto fastAppend = [&](const void* source, size_t size) { + if (size == 0) return; + const size_t oldSize = buffer.size(); + buffer.resize(oldSize + size); + std::memcpy(buffer.data() + oldSize, source, size); + }; + + // 4. Sequential Write (The TLV Pattern) + fastAppend(&typeSize, sizeof(typeSize)); + fastAppend(typeName.data(), typeSize); + fastAppend(&tagSize, sizeof(tagSize)); + fastAppend(tag.data(), tagSize); + fastAppend(&dataSize, sizeof(dataSize)); + fastAppend(jsonStr.data(), dataSize); + + // 5. Logging Optimization: Only log metadata, never hex-dump in production loops + LOG_CONTEXT(LogLevel::DEBUG, "Buffered binary for tag: " + tag, {}); + } if (!AtomicFileWriter::writeAtomicallyBinary(filename, buffer)) { @@ -780,6 +994,7 @@ bool ItemManager::importFromFile_Binary(const std::string& filename) { redoQueue = {}; items.clear(); + while (in.peek() != EOF) { uint32_t typeSize = 0, tagSize = 0, dataSize = 0; @@ -853,6 +1068,70 @@ bool ItemManager::importFromFile_Binary(const std::string& filename) { } } + + // 1. Move buffers outside the loop to reuse memory (Prevents thousands of heap allocations) + std::string typeBuf, tagBuf, dataBuf; + std::unordered_map demangledCache; + uint32_t typeSize, tagSize, dataSize; + + // 2. Use the return value of read() directly for the loop condition + while (in.read(reinterpret_cast(&typeSize), sizeof(typeSize))) + { + + // Read Type + typeBuf.resize(typeSize); + if (!in.read(typeBuf.data(), typeSize)) break; + + // Read Tag + if (!in.read(reinterpret_cast(&tagSize), sizeof(tagSize))) break; + tagBuf.resize(tagSize); + if (!in.read(tagBuf.data(), tagSize)) break; + + // Read Data + if (!in.read(reinterpret_cast(&dataSize), sizeof(dataSize))) break; + dataBuf.resize(dataSize); + if (!in.read(dataBuf.data(), dataSize)) break; + + // 3. Optimization: Cache demangled names (demangling is expensive) + if (demangledCache.find(typeBuf) == demangledCache.end()) { + demangledCache[typeBuf] = demangleType(typeBuf); + } + const std::string& prettyType = demangledCache[typeBuf]; + + // 4. Removed Hex Dump (Massive Bottleneck) + // Writing to terminal is ~100x slower than memory operations. + // If needed, only do this if LogLevel is explicitly set to DEBUG. + + try { + // 5. Parse JSON directly from the reused string buffer + json serialized = json::parse(dataBuf); + + // Use value() to avoid double lookups with contains() + int version = serialized.value("version", 1); + + if (!serialized.contains("id")) { + serialized["id"] = tagBuf; + } + + // Migration + json upgraded = migrationRegistry.upgradeToLatest(typeBuf, version, serialized); + + // 6. Fast Deserializer Lookup + auto desIt = deserializers.find(typeBuf); + if (desIt == deserializers.end()) [[unlikely]] { + LOG_CONTEXT(LogLevel::WARNING, "Unknown type: " + prettyType, {}); + continue; + } + + if (auto object = desIt->second(upgraded, tagBuf)) { + items[tagBuf] = std::move(object); + LOG_CONTEXT(LogLevel::INFO, "Imported " + tagBuf + " [" + prettyType + "]", {}); + } + + } catch (const std::exception& e) { + LOG_CONTEXT(LogLevel::ERR, "Failed tag '" + tagBuf + "': " + e.what(), {}); + } + } in.close(); LOG_CONTEXT(LogLevel::INFO, "Binary import from '" + filename + "' completed successfully with " + std::to_string(items.size()) + " items.", true); return true; @@ -887,6 +1166,7 @@ std::shared_ptr ItemManager::importSingleObject_Binary(const std::stri } + while (in.peek() != EOF) { uint32_t typeSize = 0, tagSize = 0, dataSize = 0; @@ -966,6 +1246,60 @@ std::shared_ptr ItemManager::importSingleObject_Binary(const std::stri LOG_CONTEXT(LogLevel::INFO, "Successfully imported object with tag '" + tag + "' from file '" + filename + "'", {}); return object; + + // 1. Buffers declared outside the loop to reuse memory + std::string entryType, entryTag; + uint32_t typeSize = 0, tagSize = 0, dataSize = 0; + + while (in.read(reinterpret_cast(&typeSize), sizeof(typeSize))) { + // 2. Read Metadata: Type + entryType.resize(typeSize); + in.read(entryType.data(), typeSize); + + // 3. Read Metadata: Tag + in.read(reinterpret_cast(&tagSize), sizeof(tagSize)); + entryTag.resize(tagSize); + in.read(entryTag.data(), tagSize); + + // 4. Read Data Size (But don't read the data yet!) + in.read(reinterpret_cast(&dataSize), sizeof(dataSize)); + + // 5. SMART SKIP: Check if this is NOT the one we want + if (entryType != type || entryTag != tag) { + // Skip the data bytes entirely at the file pointer level + in.seekg(dataSize, std::ios::cur); + continue; + } + + // --- MATCH FOUND --- + // Only now do we allocate memory and read the JSON content + std::string jsonStr(dataSize, '\0'); + in.read(jsonStr.data(), dataSize); + + try { + json serialized = json::parse(jsonStr); + + // Handle ID injection and Versioning efficiently + if (!serialized.contains("id")) serialized["id"] = tag; + int version = serialized.value("version", 1); + + // Migration and Deserialization + json upgraded = migrationRegistry.upgradeToLatest(entryType, version, serialized); + + auto it = deserializers.find(entryType); + if (it != deserializers.end()) { + auto object = it->second(upgraded, tag); + if (object) { + undoHistory.push_back(cloneCurrentState()); + redoQueue = {}; + items[tag] = object; + return object; + } + } + } catch (const std::exception& e) { + LOG_CONTEXT(LogLevel::ERR, "Import failure: " + std::string(e.what()), {}); + return nullptr; + } } @@ -990,6 +1324,7 @@ void ItemManager::asyncImportSingleObject_Binary(const std::string& filename, bool ItemManager::exportToFile_XML(const std::string& filename) const { + if (filename.empty()) { LOG_CONTEXT(LogLevel::ERR, "Cannot export to empty filename.", ErrorCode::INVALID_INPUT ); } @@ -1057,6 +1392,70 @@ bool ItemManager::exportToFile_XML(const std::string& filename) const { } LOG_CONTEXT(LogLevel::INFO, "XML export completed successfully to file: " + filename, true); + + if (filename.empty()) [[unlikely]] + { + LOG_CONTEXT(LogLevel::ERR, "Cannot export to empty filename.", ErrorCode::INVALID_INPUT); + return false; + } + + if (items.empty()) + { + LOG_CONTEXT(LogLevel::WARNING, "No items found for XML export to: " + filename, {}); + } + + tinyxml2::XMLDocument doc; + // Standard XML Declaration + doc.InsertFirstChild(doc.NewDeclaration()); + + auto* root = doc.NewElement("SmartStore"); + doc.InsertEndChild(root); + + for (const auto& [tag, item] : items) + { + if (!item) [[unlikely]] continue; + + auto* itemElement = doc.NewElement("Item"); + root->InsertEndChild(itemElement); + + // 1. Setting Attributes vs Elements + // Attributes are faster to parse and more compact for metadata + itemElement->SetAttribute("tag", tag.c_str()); + itemElement->SetAttribute("type", item->getTypeName().c_str()); + + // 2. Optimized JSON Wrapping + nlohmann::json wrapped; + wrapped["id"] = item->getId(); + // Avoid redundant "tag" and "type" keys inside the JSON if they are already in XML attributes + + nlohmann::json userData = item->toJson(); + wrapped["data"] = userData.is_object() ? std::move(userData) : nlohmann::json{{"value", userData}}; + + // 3. Fast Serialization: Avoid std::ostringstream + // nlohmann::json::dump() is significantly faster than ostringstream operator<< + std::string jsonStr = wrapped.dump(); + + auto* dataElement = doc.NewElement("Data"); + dataElement->SetText(jsonStr.c_str()); + itemElement->InsertEndChild(dataElement); + + // Debug logging only in non-production environments + #ifdef _DEBUG + LOG_CONTEXT(LogLevel::INFO, "Exported item: " + tag, {}); + #endif + } + + // 4. Efficient Printing + tinyxml2::XMLPrinter printer; + doc.Accept(&printer); // Accept is often more robust than Print() + + if (!AtomicFileWriter::writeAtomically(filename, printer.CStr())) { + LOG_CONTEXT(LogLevel::ERR, "Failed atomic XML write: " + filename, false); + return false; + } + + LOG_CONTEXT(LogLevel::INFO, "XML export successful: " + filename, true); + return true; } @@ -1192,6 +1591,7 @@ void ItemManager::asyncImportFromFile_XML(const std::string& filename) { std::optional> ItemManager::importSingleObject_XML(const std::string& filename, const std::string& type, + const std::string& tag) { if (filename.empty()) { @@ -1203,17 +1603,28 @@ std::optional> ItemManager::importSingleObject_XML(con " with type '" + demangleType(type) + "' and tag '" + tag + "'", {}); if (filename.empty()) { + + const std::string& tag) +{ + + if (filename.empty()) [[unlikely]] { + LOG_CONTEXT(LogLevel::ERR, "Filename is empty — cannot import from XML.", {}); return std::nullopt; } tinyxml2::XMLDocument doc; + if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) { + + if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) [[unlikely]] { + LOG_CONTEXT(LogLevel::ERR, "Failed to load XML file: " + filename, {}); return std::nullopt; } auto* root = doc.FirstChildElement("SmartStore"); + if (!root) { LOG_CONTEXT(LogLevel::ERR, "Missing root element in XML file: " + filename, {}); return std::nullopt; @@ -1267,12 +1678,77 @@ std::optional> ItemManager::importSingleObject_XML(con return item; } catch (const std::exception& e) { LOG_CONTEXT(LogLevel::ERR, "Failed to parse JSON data for tag '" + tag + "': " + e.what(), {}); + + if (!root) [[unlikely]] { + LOG_CONTEXT(LogLevel::ERR, "Missing root in: " + filename, {}); + return std::nullopt; + } + + // Pre-check for deserializer availability before entering the heavy loop + auto desIt = deserializers.find(type); + if (desIt == deserializers.end()) [[unlikely]] { + LOG_CONTEXT(LogLevel::ERR, "No deserializer registered for type: " + demangleType(type), {}); + return std::nullopt; + } + + // Linear Search through XML items + for (auto* itemElement = root->FirstChildElement("Item"); itemElement; itemElement = itemElement->NextSiblingElement("Item")) { + + // 1. Efficient Lookups + // Use Attribute checks if you adopted the optimization, + // otherwise, use ChildElement as you have below. + auto* tagElement = itemElement->FirstChildElement("Tag"); + auto* typeElement = itemElement->FirstChildElement("Type"); + + if (!tagElement || !typeElement) continue; + + const char* tagText = tagElement->GetText(); + const char* typeText = typeElement->GetText(); + + // 2. String Comparison without allocation + // Comparing raw char pointers (strcmp) is faster than std::string(tagText) == tag + if (!tagText || !typeText || std::strcmp(tagText, tag.c_str()) != 0 || std::strcmp(typeText, type.c_str()) != 0) { + continue; + } + + // 3. We have a match, now extract the heavier data element + auto* dataElement = itemElement->FirstChildElement("Data"); + const char* dataText = dataElement ? dataElement->GetText() : nullptr; + if (!dataText) continue; + + try { + // 4. Parse directly from char pointer + nlohmann::json j = nlohmann::json::parse(dataText); + + // Ensure metadata is synchronized + if (!j.contains("id")) j["id"] = tagText; + if (!j.contains("tag")) j["tag"] = tagText; + + int version = j.value("version", 1); + json upgraded = migrationRegistry.upgradeToLatest(type, version, j); + + // 5. Build and State Update + auto item = desIt->second(upgraded, tag); + if (item) { + undoHistory.push_back(cloneCurrentState()); + redoQueue = {}; + items[tag] = item; + + LOG_CONTEXT(LogLevel::INFO, "Imported item: " + tag, {}); + return item; + } + } catch (const std::exception& e) { + LOG_CONTEXT(LogLevel::ERR, "XML Parse/Upgrade error: " + std::string(e.what()), {}); + return std::nullopt; } } LOG_CONTEXT(LogLevel::INFO, "No matching item found for tag '" + tag + "' and type '" + demangleType(type) + "' in XML file: " + filename, {}); + + LOG_CONTEXT(LogLevel::WARNING, "Item not found in XML: " + tag, {}); + return std::nullopt; } diff --git a/src/t_wrapper/ItemWrapper.h b/src/t_wrapper/ItemWrapper.h index f219d0b..2b1e8e2 100644 --- a/src/t_wrapper/ItemWrapper.h +++ b/src/t_wrapper/ItemWrapper.h @@ -47,10 +47,16 @@ using json = nlohmann::json; namespace IdProvider { inline std::string generateId() { + + inline std::string generateId() + { + static constexpr char hex_chars[] = "0123456789abcdef"; + static std::random_device rd; static std::mt19937 gen(rd()); static std::uniform_int_distribution<> dis(0, 15); + std::stringstream ss; ss << "obj_"; for (int i = 0; i < 8; ++i) ss << std::hex << dis(gen); @@ -64,6 +70,25 @@ namespace IdProvider { ss << '-'; for (int i = 0; i < 12; ++i) ss << std::hex << dis(gen); return ss.str(); + + // Pre-allocate the string to avoid multiple reallocations + std::string ss; + ss.reserve(36); // UUID length + ss += "obj_"; + + auto add_hex = [&](int count) + { + for (int i = 0; i < count; ++i) ss += hex_chars[dis(gen)]; + }; + + add_hex(8); ss += '-'; + add_hex(4); ss += "-4"; // v4 + add_hex(3); ss += '-'; + ss += hex_chars[(dis(gen) & 0x3) | 0x8]; // variant + add_hex(3); ss += '-'; + add_hex(12); + return ss; + } } From cb42df4c58dd3dc7bfc5c36eaf99e6a5d8b36570 Mon Sep 17 00:00:00 2001 From: Moin Shaikh Date: Mon, 29 Dec 2025 02:00:06 +0530 Subject: [PATCH 12/16] undo --- src/t_manager/ItemManager.tpp | 476 ---------------------------------- src/t_wrapper/ItemWrapper.h | 25 -- 2 files changed, 501 deletions(-) diff --git a/src/t_manager/ItemManager.tpp b/src/t_manager/ItemManager.tpp index 82b42a7..7ba3331 100644 --- a/src/t_manager/ItemManager.tpp +++ b/src/t_manager/ItemManager.tpp @@ -463,10 +463,6 @@ void ItemManager::asyncExportToFile_Json(const std::string& filename) const { }).detach(); // Fire-and-forget } - - -/* - void ItemManager::importFromFile_Json(const std::string& filename) { if (filename.empty()) { @@ -564,110 +560,6 @@ void ItemManager::importFromFile_Json(const std::string& filename) { LOG_CONTEXT(LogLevel::INFO, "Completed import of " + std::to_string(importCount) + " item(s) from JSON file: " + filename, {}); } -<<<<<<< HEAD - -======= -*/ - -void ItemManager::importFromFile_Json(const std::string& filename) { - - if (filename.empty()) { - LOG_CONTEXT(LogLevel::ERR, "Cannot import from empty filename.", ErrorCode::ITEM_NOT_FOUND); - } - - LOG_CONTEXT(LogLevel::INFO, "Attempting JSON import from file: " + filename, {}); - - std::ifstream in(filename); - if (!in) { - LOG_CONTEXT(LogLevel::ERR, "Cannot open file for reading: " + filename, ErrorCode::FILE_LOAD_FAILED); - } - - json parsedJson; - in >> parsedJson; - - std::cout << Logger::getColorCode(LogColor::CYAN) + "\n:::| Loaded JSON content from file:\n" - << Logger::getColorCode(LogColor::RESET) << parsedJson.dump(2) << "\n"; - LOG_CONTEXT(LogLevel::DEBUG, "JSON file loaded successfully: " + filename, {}); - - if (parsedJson.is_array()) { - LOG_CONTEXT(LogLevel::DEBUG, "Processing JSON array format.", {}); - } else if (parsedJson.contains("items") && parsedJson["items"].is_array()) { - parsedJson = parsedJson["items"]; - LOG_CONTEXT(LogLevel::DEBUG, "Processing JSON with 'items' key.", {}); - } else { - LOG_CONTEXT(LogLevel::ERR, "", std::make_exception_ptr(std::runtime_error( - "Invalid JSON format: " + filename + " Expected an array or 'items' key."))); - } - - undoHistory.push_back(cloneCurrentState()); - redoQueue = {}; - items.clear(); - - int importCount = 0; - - std::unordered_map demangledCache; - std::unordered_map desCache; - - for (const auto& entry : parsedJson) { - // 2. Use find() to get iterators – this is the fastest way to check existence and get data - auto itTag = entry.find("tag"); - auto itType = entry.find("type"); - auto itData = entry.find("data"); - - // Skip if any essential key is missing - if (itTag == entry.end() || itType == entry.end() || itData == entry.end()) continue; - - // 3. Zero-copy string access: get a reference directly from the JSON memory - const std::string& tag = itTag->get_ref(); - const std::string& typeName = itType->get_ref(); - - // 4. Handle Demangling Cache (CPU Optimization) - if (demangledCache.find(typeName) == demangledCache.end()) { - demangledCache[typeName] = demangleType(typeName); - } - const std::string& prettyName = demangledCache[typeName]; - - // 5. Handle Deserializer Cache (Lookup Optimization) - auto cachedDesIt = desCache.find(typeName); - if (cachedDesIt == desCache.end()) { - auto globalIt = deserializers.find(typeName); - desCache[typeName] = (globalIt != deserializers.end()) ? globalIt->second : nullptr; - cachedDesIt = desCache.find(typeName); - } - - if (!cachedDesIt->second) { - LOG_CONTEXT(LogLevel::WARNING, "No deserializer for: " + prettyName, {}); - continue; - } - - // 6. Process Data - int version = entry.value("version", 1); - json rawData = *itData; // Copy once for migration - - if (auto itId = entry.find("id"); itId != entry.end() && !rawData.contains("id")) { - rawData["id"] = *itId; - } - - // 7. Schema Registry: Only store if we haven't seen this type in this session - if (auto itSchema = entry.find("schema"); itSchema != entry.end()) { - schemaRegistry.try_emplace(typeName, [schema = *itSchema]() { return schema; }); - } - - try { - json upgraded = migrationRegistry.upgradeToLatest(typeName, version, rawData); - - // Use the cached function pointer to deserialize - if (auto newItem = cachedDesIt->second(upgraded, tag)) { - items[tag] = std::move(newItem); - ++importCount; - } - } catch (const std::exception& e) { - LOG_CONTEXT(LogLevel::ERR, "Import fail [" + tag + "]: " + e.what(), {}); - } - } - - LOG_CONTEXT(LogLevel::INFO, "Completed import of " + std::to_string(importCount) + " item(s) from JSON file: " + filename, {}); -} void ItemManager::asyncImportFromFile_Json(const std::string& filename) { std::thread([this, filename]() { @@ -705,7 +597,6 @@ std::shared_ptr ItemManager::importSingleObject_Json(const std::string } - for (const auto& entry : array) { if (entry.value("tag", "") == tag && entry.value("type", "") == typeName) { @@ -762,71 +653,6 @@ std::shared_ptr ItemManager::importSingleObject_Json(const std::string LOG_CONTEXT(LogLevel::ERR, "", std::make_exception_ptr(std::runtime_error( "Exception during deserialization of '" + tag + "': " + e.what()))); } - - // Pre-demangle the target type name once before entering the loop - const std::string targetTypeNameDemangled = demangleType(typeName); - - for (const auto& entry : array) { - // 1. Efficient key access using find() to avoid double lookups - auto itTag = entry.find("tag"); - auto itType = entry.find("type"); - - if (itTag == entry.end() || itType == entry.end()) continue; - - // 2. Zero-copy comparison using get_ref to avoid string allocations - if (itTag->get_ref() == tag && - itType->get_ref() == typeName) { - - LOG_CONTEXT(LogLevel::INFO, "Found match: '" + tag + "' [" + targetTypeNameDemangled + "]", {}); - - // 3. Extract version and data pointer - int version = entry.value("version", 1); - auto itData = entry.find("data"); - if (itData == entry.end()) { - LOG_CONTEXT(LogLevel::ERR, "Object found but 'data' key is missing.", {}); - return nullptr; - } - - // Copy rawData once for potential mutation/migration - json rawData = *itData; - - // 4. Inject ID if it exists at the entry level but not inside data - if (auto itId = entry.find("id"); itId != entry.end()) { - if (!rawData.contains("id")) { - rawData["id"] = *itId; - } - } - - // 5. Schema Registration (using try_emplace to avoid redundant lambda creation) - if (auto itSchema = entry.find("schema"); itSchema != entry.end()) { - schemaRegistry.try_emplace(typeName, [s = *itSchema]() { return s; }); - } - - // 6. Migration and Deserialization - try { - json upgraded = migrationRegistry.upgradeToLatest(typeName, version, rawData); - - // Check deserializer once - auto desIt = deserializers.find(typeName); - if (desIt == deserializers.end()) [[unlikely]] { - LOG_CONTEXT(LogLevel::ERR, "No deserializer for type: " + targetTypeNameDemangled, {}); - return nullptr; - } - - auto item = desIt->second(upgraded, tag); - if (item) { - // Successful path - undoHistory.push_back(cloneCurrentState()); - redoQueue = {}; - items[tag] = item; - return item; - } - } catch (const std::exception& e) { - LOG_CONTEXT(LogLevel::ERR, "Deserialization error for '" + tag + "': " + e.what(), {}); - } - - return nullptr; // Stop searching once target is found, even if it failed - } } @@ -865,7 +691,6 @@ bool ItemManager::exportToFile_Binary(const std::string& filename) const { std::vector buffer; - for (const auto& [tag, item] : items) { json serializedJson = item->serialize(); serializedJson["id"] = item->getId(); @@ -909,45 +734,6 @@ bool ItemManager::exportToFile_Binary(const std::string& filename) const { dumpHex(tagStr.data(), tagSize); dumpHex(&dataSize, sizeof(dataSize)); dumpHex(jsonStr.data(), dataSize); - - buffer.reserve(items.size() * 512); - - for (const auto& [tag, item] : items) { - if (!item) [[unlikely]] continue; - - // 2. Serialize and prepare metadata - json serializedJson = item->serialize(); - serializedJson["id"] = item->getId(); - serializedJson["tag"] = tag; - serializedJson["type"] = item->getTypeName(); - - const std::string& typeName = item->getTypeName(); - const std::string jsonStr = serializedJson.dump(); - - uint32_t typeSize = static_cast(typeName.size()); - uint32_t tagSize = static_cast(tag.size()); - uint32_t dataSize = static_cast(jsonStr.size()); - - // 3. High-speed Append logic using std::memcpy - // This avoids the overhead of buffer.insert (which checks capacity every time) - auto fastAppend = [&](const void* source, size_t size) { - if (size == 0) return; - const size_t oldSize = buffer.size(); - buffer.resize(oldSize + size); - std::memcpy(buffer.data() + oldSize, source, size); - }; - - // 4. Sequential Write (The TLV Pattern) - fastAppend(&typeSize, sizeof(typeSize)); - fastAppend(typeName.data(), typeSize); - fastAppend(&tagSize, sizeof(tagSize)); - fastAppend(tag.data(), tagSize); - fastAppend(&dataSize, sizeof(dataSize)); - fastAppend(jsonStr.data(), dataSize); - - // 5. Logging Optimization: Only log metadata, never hex-dump in production loops - LOG_CONTEXT(LogLevel::DEBUG, "Buffered binary for tag: " + tag, {}); - } if (!AtomicFileWriter::writeAtomicallyBinary(filename, buffer)) { @@ -994,7 +780,6 @@ bool ItemManager::importFromFile_Binary(const std::string& filename) { redoQueue = {}; items.clear(); - while (in.peek() != EOF) { uint32_t typeSize = 0, tagSize = 0, dataSize = 0; @@ -1068,70 +853,6 @@ bool ItemManager::importFromFile_Binary(const std::string& filename) { } } - - // 1. Move buffers outside the loop to reuse memory (Prevents thousands of heap allocations) - std::string typeBuf, tagBuf, dataBuf; - std::unordered_map demangledCache; - uint32_t typeSize, tagSize, dataSize; - - // 2. Use the return value of read() directly for the loop condition - while (in.read(reinterpret_cast(&typeSize), sizeof(typeSize))) - { - - // Read Type - typeBuf.resize(typeSize); - if (!in.read(typeBuf.data(), typeSize)) break; - - // Read Tag - if (!in.read(reinterpret_cast(&tagSize), sizeof(tagSize))) break; - tagBuf.resize(tagSize); - if (!in.read(tagBuf.data(), tagSize)) break; - - // Read Data - if (!in.read(reinterpret_cast(&dataSize), sizeof(dataSize))) break; - dataBuf.resize(dataSize); - if (!in.read(dataBuf.data(), dataSize)) break; - - // 3. Optimization: Cache demangled names (demangling is expensive) - if (demangledCache.find(typeBuf) == demangledCache.end()) { - demangledCache[typeBuf] = demangleType(typeBuf); - } - const std::string& prettyType = demangledCache[typeBuf]; - - // 4. Removed Hex Dump (Massive Bottleneck) - // Writing to terminal is ~100x slower than memory operations. - // If needed, only do this if LogLevel is explicitly set to DEBUG. - - try { - // 5. Parse JSON directly from the reused string buffer - json serialized = json::parse(dataBuf); - - // Use value() to avoid double lookups with contains() - int version = serialized.value("version", 1); - - if (!serialized.contains("id")) { - serialized["id"] = tagBuf; - } - - // Migration - json upgraded = migrationRegistry.upgradeToLatest(typeBuf, version, serialized); - - // 6. Fast Deserializer Lookup - auto desIt = deserializers.find(typeBuf); - if (desIt == deserializers.end()) [[unlikely]] { - LOG_CONTEXT(LogLevel::WARNING, "Unknown type: " + prettyType, {}); - continue; - } - - if (auto object = desIt->second(upgraded, tagBuf)) { - items[tagBuf] = std::move(object); - LOG_CONTEXT(LogLevel::INFO, "Imported " + tagBuf + " [" + prettyType + "]", {}); - } - - } catch (const std::exception& e) { - LOG_CONTEXT(LogLevel::ERR, "Failed tag '" + tagBuf + "': " + e.what(), {}); - } - } in.close(); LOG_CONTEXT(LogLevel::INFO, "Binary import from '" + filename + "' completed successfully with " + std::to_string(items.size()) + " items.", true); return true; @@ -1166,7 +887,6 @@ std::shared_ptr ItemManager::importSingleObject_Binary(const std::stri } - while (in.peek() != EOF) { uint32_t typeSize = 0, tagSize = 0, dataSize = 0; @@ -1246,60 +966,6 @@ std::shared_ptr ItemManager::importSingleObject_Binary(const std::stri LOG_CONTEXT(LogLevel::INFO, "Successfully imported object with tag '" + tag + "' from file '" + filename + "'", {}); return object; - - // 1. Buffers declared outside the loop to reuse memory - std::string entryType, entryTag; - uint32_t typeSize = 0, tagSize = 0, dataSize = 0; - - while (in.read(reinterpret_cast(&typeSize), sizeof(typeSize))) { - // 2. Read Metadata: Type - entryType.resize(typeSize); - in.read(entryType.data(), typeSize); - - // 3. Read Metadata: Tag - in.read(reinterpret_cast(&tagSize), sizeof(tagSize)); - entryTag.resize(tagSize); - in.read(entryTag.data(), tagSize); - - // 4. Read Data Size (But don't read the data yet!) - in.read(reinterpret_cast(&dataSize), sizeof(dataSize)); - - // 5. SMART SKIP: Check if this is NOT the one we want - if (entryType != type || entryTag != tag) { - // Skip the data bytes entirely at the file pointer level - in.seekg(dataSize, std::ios::cur); - continue; - } - - // --- MATCH FOUND --- - // Only now do we allocate memory and read the JSON content - std::string jsonStr(dataSize, '\0'); - in.read(jsonStr.data(), dataSize); - - try { - json serialized = json::parse(jsonStr); - - // Handle ID injection and Versioning efficiently - if (!serialized.contains("id")) serialized["id"] = tag; - int version = serialized.value("version", 1); - - // Migration and Deserialization - json upgraded = migrationRegistry.upgradeToLatest(entryType, version, serialized); - - auto it = deserializers.find(entryType); - if (it != deserializers.end()) { - auto object = it->second(upgraded, tag); - if (object) { - undoHistory.push_back(cloneCurrentState()); - redoQueue = {}; - items[tag] = object; - return object; - } - } - } catch (const std::exception& e) { - LOG_CONTEXT(LogLevel::ERR, "Import failure: " + std::string(e.what()), {}); - return nullptr; - } } @@ -1324,7 +990,6 @@ void ItemManager::asyncImportSingleObject_Binary(const std::string& filename, bool ItemManager::exportToFile_XML(const std::string& filename) const { - if (filename.empty()) { LOG_CONTEXT(LogLevel::ERR, "Cannot export to empty filename.", ErrorCode::INVALID_INPUT ); } @@ -1392,70 +1057,6 @@ bool ItemManager::exportToFile_XML(const std::string& filename) const { } LOG_CONTEXT(LogLevel::INFO, "XML export completed successfully to file: " + filename, true); - - if (filename.empty()) [[unlikely]] - { - LOG_CONTEXT(LogLevel::ERR, "Cannot export to empty filename.", ErrorCode::INVALID_INPUT); - return false; - } - - if (items.empty()) - { - LOG_CONTEXT(LogLevel::WARNING, "No items found for XML export to: " + filename, {}); - } - - tinyxml2::XMLDocument doc; - // Standard XML Declaration - doc.InsertFirstChild(doc.NewDeclaration()); - - auto* root = doc.NewElement("SmartStore"); - doc.InsertEndChild(root); - - for (const auto& [tag, item] : items) - { - if (!item) [[unlikely]] continue; - - auto* itemElement = doc.NewElement("Item"); - root->InsertEndChild(itemElement); - - // 1. Setting Attributes vs Elements - // Attributes are faster to parse and more compact for metadata - itemElement->SetAttribute("tag", tag.c_str()); - itemElement->SetAttribute("type", item->getTypeName().c_str()); - - // 2. Optimized JSON Wrapping - nlohmann::json wrapped; - wrapped["id"] = item->getId(); - // Avoid redundant "tag" and "type" keys inside the JSON if they are already in XML attributes - - nlohmann::json userData = item->toJson(); - wrapped["data"] = userData.is_object() ? std::move(userData) : nlohmann::json{{"value", userData}}; - - // 3. Fast Serialization: Avoid std::ostringstream - // nlohmann::json::dump() is significantly faster than ostringstream operator<< - std::string jsonStr = wrapped.dump(); - - auto* dataElement = doc.NewElement("Data"); - dataElement->SetText(jsonStr.c_str()); - itemElement->InsertEndChild(dataElement); - - // Debug logging only in non-production environments - #ifdef _DEBUG - LOG_CONTEXT(LogLevel::INFO, "Exported item: " + tag, {}); - #endif - } - - // 4. Efficient Printing - tinyxml2::XMLPrinter printer; - doc.Accept(&printer); // Accept is often more robust than Print() - - if (!AtomicFileWriter::writeAtomically(filename, printer.CStr())) { - LOG_CONTEXT(LogLevel::ERR, "Failed atomic XML write: " + filename, false); - return false; - } - - LOG_CONTEXT(LogLevel::INFO, "XML export successful: " + filename, true); - return true; } @@ -1591,7 +1192,6 @@ void ItemManager::asyncImportFromFile_XML(const std::string& filename) { std::optional> ItemManager::importSingleObject_XML(const std::string& filename, const std::string& type, - const std::string& tag) { if (filename.empty()) { @@ -1603,28 +1203,17 @@ std::optional> ItemManager::importSingleObject_XML(con " with type '" + demangleType(type) + "' and tag '" + tag + "'", {}); if (filename.empty()) { - - const std::string& tag) -{ - - if (filename.empty()) [[unlikely]] { - LOG_CONTEXT(LogLevel::ERR, "Filename is empty — cannot import from XML.", {}); return std::nullopt; } tinyxml2::XMLDocument doc; - if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) { - - if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) [[unlikely]] { - LOG_CONTEXT(LogLevel::ERR, "Failed to load XML file: " + filename, {}); return std::nullopt; } auto* root = doc.FirstChildElement("SmartStore"); - if (!root) { LOG_CONTEXT(LogLevel::ERR, "Missing root element in XML file: " + filename, {}); return std::nullopt; @@ -1678,77 +1267,12 @@ std::optional> ItemManager::importSingleObject_XML(con return item; } catch (const std::exception& e) { LOG_CONTEXT(LogLevel::ERR, "Failed to parse JSON data for tag '" + tag + "': " + e.what(), {}); - - if (!root) [[unlikely]] { - LOG_CONTEXT(LogLevel::ERR, "Missing root in: " + filename, {}); - return std::nullopt; - } - - // Pre-check for deserializer availability before entering the heavy loop - auto desIt = deserializers.find(type); - if (desIt == deserializers.end()) [[unlikely]] { - LOG_CONTEXT(LogLevel::ERR, "No deserializer registered for type: " + demangleType(type), {}); - return std::nullopt; - } - - // Linear Search through XML items - for (auto* itemElement = root->FirstChildElement("Item"); itemElement; itemElement = itemElement->NextSiblingElement("Item")) { - - // 1. Efficient Lookups - // Use Attribute checks if you adopted the optimization, - // otherwise, use ChildElement as you have below. - auto* tagElement = itemElement->FirstChildElement("Tag"); - auto* typeElement = itemElement->FirstChildElement("Type"); - - if (!tagElement || !typeElement) continue; - - const char* tagText = tagElement->GetText(); - const char* typeText = typeElement->GetText(); - - // 2. String Comparison without allocation - // Comparing raw char pointers (strcmp) is faster than std::string(tagText) == tag - if (!tagText || !typeText || std::strcmp(tagText, tag.c_str()) != 0 || std::strcmp(typeText, type.c_str()) != 0) { - continue; - } - - // 3. We have a match, now extract the heavier data element - auto* dataElement = itemElement->FirstChildElement("Data"); - const char* dataText = dataElement ? dataElement->GetText() : nullptr; - if (!dataText) continue; - - try { - // 4. Parse directly from char pointer - nlohmann::json j = nlohmann::json::parse(dataText); - - // Ensure metadata is synchronized - if (!j.contains("id")) j["id"] = tagText; - if (!j.contains("tag")) j["tag"] = tagText; - - int version = j.value("version", 1); - json upgraded = migrationRegistry.upgradeToLatest(type, version, j); - - // 5. Build and State Update - auto item = desIt->second(upgraded, tag); - if (item) { - undoHistory.push_back(cloneCurrentState()); - redoQueue = {}; - items[tag] = item; - - LOG_CONTEXT(LogLevel::INFO, "Imported item: " + tag, {}); - return item; - } - } catch (const std::exception& e) { - LOG_CONTEXT(LogLevel::ERR, "XML Parse/Upgrade error: " + std::string(e.what()), {}); - return std::nullopt; } } LOG_CONTEXT(LogLevel::INFO, "No matching item found for tag '" + tag + "' and type '" + demangleType(type) + "' in XML file: " + filename, {}); - - LOG_CONTEXT(LogLevel::WARNING, "Item not found in XML: " + tag, {}); - return std::nullopt; } diff --git a/src/t_wrapper/ItemWrapper.h b/src/t_wrapper/ItemWrapper.h index 2b1e8e2..f219d0b 100644 --- a/src/t_wrapper/ItemWrapper.h +++ b/src/t_wrapper/ItemWrapper.h @@ -47,16 +47,10 @@ using json = nlohmann::json; namespace IdProvider { inline std::string generateId() { - - inline std::string generateId() - { - static constexpr char hex_chars[] = "0123456789abcdef"; - static std::random_device rd; static std::mt19937 gen(rd()); static std::uniform_int_distribution<> dis(0, 15); - std::stringstream ss; ss << "obj_"; for (int i = 0; i < 8; ++i) ss << std::hex << dis(gen); @@ -70,25 +64,6 @@ namespace IdProvider { ss << '-'; for (int i = 0; i < 12; ++i) ss << std::hex << dis(gen); return ss.str(); - - // Pre-allocate the string to avoid multiple reallocations - std::string ss; - ss.reserve(36); // UUID length - ss += "obj_"; - - auto add_hex = [&](int count) - { - for (int i = 0; i < count; ++i) ss += hex_chars[dis(gen)]; - }; - - add_hex(8); ss += '-'; - add_hex(4); ss += "-4"; // v4 - add_hex(3); ss += '-'; - ss += hex_chars[(dis(gen) & 0x3) | 0x8]; // variant - add_hex(3); ss += '-'; - add_hex(12); - return ss; - } } From 98003a359a88cd572318cdf9d52b993de3287085 Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Sat, 10 Jan 2026 15:36:25 +0530 Subject: [PATCH 13/16] Add mutex lock for file reading in ItemManager --- src/t_manager/ItemManager.tpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t_manager/ItemManager.tpp b/src/t_manager/ItemManager.tpp index 7ba3331..fb01384 100644 --- a/src/t_manager/ItemManager.tpp +++ b/src/t_manager/ItemManager.tpp @@ -475,7 +475,7 @@ void ItemManager::importFromFile_Json(const std::string& filename) { if (!in) { LOG_CONTEXT(LogLevel::ERR, "Cannot open file for reading: " + filename, ErrorCode::FILE_LOAD_FAILED); } - + std::lock_guard lock(mutex_); json parsedJson; in >> parsedJson; From 113c0a52813b84b82c8c173f9cda85031ebec440 Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Sat, 10 Jan 2026 15:39:39 +0530 Subject: [PATCH 14/16] Add mutex lock for thread safety in ItemManager --- src/t_manager/ItemManager.tpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t_manager/ItemManager.tpp b/src/t_manager/ItemManager.tpp index fb01384..a293495 100644 --- a/src/t_manager/ItemManager.tpp +++ b/src/t_manager/ItemManager.tpp @@ -775,7 +775,7 @@ bool ItemManager::importFromFile_Binary(const std::string& filename) { LOG_CONTEXT(LogLevel::ERR, "Cannot open binary file '" + filename + "' for reading.", false); return false; } - + std::lock_guard lock(mutex_); undoHistory.push_back(cloneCurrentState()); redoQueue = {}; items.clear(); From 0f62afa10e7d08c19b69740bf8ab77f1a1bf2df5 Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:48:51 +0530 Subject: [PATCH 15/16] Enhance documentation for ItemManager class Added detailed documentation comments to the ItemManager class and its methods, enhancing code clarity and usability. --- src/t_manager/ItemManager.h | 848 ++++++++++++++++++++++++++++++++---- 1 file changed, 769 insertions(+), 79 deletions(-) diff --git a/src/t_manager/ItemManager.h b/src/t_manager/ItemManager.h index 5003cc0..b3ecef2 100644 --- a/src/t_manager/ItemManager.h +++ b/src/t_manager/ItemManager.h @@ -34,46 +34,164 @@ using json = nlohmann::json; -constexpr size_t MAX_UNDO_HISTORY = 50; -constexpr size_t MAX_REDO_HISTORY = 50; - - - - -class ItemManager { +constexpr size_t MAX_UNDO_HISTORY = 50; /**< Maximum undo history states to retain */ +constexpr size_t MAX_REDO_HISTORY = 50; /**< Maximum redo history states to retain */ + + /** + * @class ItemManager + * @brief Central manager for storing, serializing, and managing items in the Smart_Store framework. + * + * ItemManager is the core component that provides: + * - **State Management**: Stores items indexed by tags with undo/redo functionality + * - **Type Registration**: Maintains type registry for serialization/deserialization + * - **Persistence**: Supports multiple formats (JSON, XML, Binary, CSV) + * - **Thread Safety**: Uses mutex for concurrent access protection + * - **Async Operations**: Provides asynchronous file I/O methods + * + * @details + * ItemManager uses a sophisticated internal architecture: + * + * **Storage Structures:** + * - @ref items: Main item storage by tag (unordered_map) + * - @ref idMap: Fast lookup by unique ID + * - @ref typeUsage: Tracks type frequency statistics + * - @ref undoHistory: Deque of previous states (limited to MAX_UNDO_HISTORY) + * - @ref redoQueue: Queue for redo operations + * + * **Type System:** + * - @ref registeredTypes: Maps type names to type_index + * - @ref schemaRegistry: Functions returning JSON schema for types + * - @ref deserializers: Functions reconstructing items from JSON + * + * **Thread Safety:** + * All public methods are thread-safe via @ref mutex_. Internal state access + * is protected with std::lock_guard. + * + * **Undo/Redo System:** + * - Maintains complete state snapshots in undoHistory deque + * - Limited to MAX_UNDO_HISTORY (default 50) to control memory + * - redoQueue tracks undone states for redo operations + * - saveState() automatically called after modifications + * + * **Serialization Support:** + * - JSON: Native support via nlohmann/json + * - XML: Requires tinyxml2 library integration + * - Binary: Custom binary format + * - CSV: Tabular export format + * + * @tparam Non-template class + * + * @note All item tags must be unique within an ItemManager instance + * @note Items are always managed via shared_ptr + * @note Type T must be wrapped in ItemWrapper before storage + * + * @author Victor + * @date 2025 + * @see ItemWrapper + * @see BaseItem + * @see GlobalItemManager + */ +class ItemManager +{ public: - // State Manager. - // This is a type alias for the state of the ItemManager, which is a map of item tags to BaseItem pointers. - // It allows for easy management of the current state, including undo and redo operations. + /** + * @typedef State + * @brief Type alias for a complete ItemManager state snapshot. + * + * Represents all items at a specific point in time, used for + * undo/redo operations. Maps user-defined tags to shared pointers + * of BaseItem objects. + */ using State = std::unordered_map>; private: - // Main storage. - // This is a map that stores all items by their tags. The tag is a unique identifier for each item. - // It allows for quick access to items by their tag, which is useful for operations like + /** + * @brief Main storage container for all items. + * + * Key: User-defined tag (unique identifier within this manager) + * Value: Shared pointer to the wrapped item (BaseItem) + * + * Uses unordered_map for O(1) average lookup by tag. + */ std::unordered_map> items; - //Queues for managing redo and undo functions. - std::deque undoHistory; // works like a queue (can trim front) - std::queue redoQueue; // replaces redoStack - - //::-> DATA STRUCTURES. - //**************************************** - - // Maps type names to their usage count. This allows tracking how many times each type is used - // This is useful for optimization and understanding which types are most common in the system + /** + * @brief History deque for undo operations. + * + * Stores complete state snapshots as a deque (not stack) to allow + * trimming from the front when exceeding MAX_UNDO_HISTORY. + * Works LIFO (Last In, First Out) semantically. + * + * @note Limited to MAX_UNDO_HISTORY entries (default 50) + * @note Oldest states are removed when limit exceeded + */ + std::deque undoHistory; + + /** + * @brief Queue for managing redo operations. + * + * Stores states that have been undone, allowing them to be + * restored via redo(). When new modifications occur, redoQueue is cleared. + * + * @note Replaces traditional redo stack with queue for FIFO semantics + */ + std::queue redoQueue; + + /** + * @brief Maps type names to their usage frequency. + * + * Tracks how many items of each type exist in the manager. + * Useful for optimization analysis and understanding data distribution. + * + * Key: Demangled type name (e.g., "int", "User") + * Value: Number of items of that type + */ std::unordered_map typeUsage; - // Maps item IDs to their BaseItem pointers for fast lookup. This allows quick access to items by their unique ID + /** + * @brief Maps unique item IDs to their corresponding BaseItem pointers. + * + * Provides fast O(1) lookup of items by their system-generated ID, + * complementing the tag-based items map. + * + * Key: Unique ID generated by IdProvider + * Value: Shared pointer to the item + * + * @note Used internally for ID-based operations + * @see ItemWrapper::id_ + */ std::unordered_map> idMap; - // Maps for managing type registration and schema. This maps type names to their std::type_index for fast lookup + /** + * @brief Maps type names to their std::type_index for identification. + * + * Enables runtime type checking and registration verification. + * + * Key: Demangled type name + * Value: std::type_index for the registered type + */ std::unordered_map registeredTypes; - // Maps type names to their schema functions. This maps type names to functions that return their schema as json + /** + * @brief Maps type names to schema-generating functions. + * + * Each registered type can provide a JSON schema describing its structure. + * Useful for validation and documentation generation. + * + * Key: Type name + * Value: Function returning JSON schema + */ std::unordered_map> schemaRegistry; - // Maps type names to their deserialization functions. This maps type names to functions that deserialize json into BaseItem pointers + /** + * @brief Maps type names to deserialization functions. + * + * Stores lambdas/functions capable of reconstructing items + * of specific types from JSON representations. + * + * Key: Type name + * Value: Function taking JSON and tag, returning shared_ptr + */ std::unordered_map(const json&, const std::string&)>> deserializers; // thread-safety gatekeeper @@ -84,22 +202,97 @@ class ItemManager { //::-> PRIVATE FUNCTIONS. //**************************************** + /** + * @brief Registry for schema version migration functions. + * + * Handles version compatibility and automatic migration of items + * when loading from older file formats. + */ MigrationRegistry migrationRegistry; - // Helper function to clone the current state of items + /** + * @brief Creates a deep copy of the current state. + * + * Clones all items to create an independent state snapshot + * used for undo/redo functionality. + * + * @return State A new State containing clones of all current items + * @note Uses BaseItem::clone() for each item + */ State cloneCurrentState() const; - //Automatic save for the redo and undo history. + /** + * @brief Saves current state to undo history. + * + * Creates a state snapshot and adds it to undoHistory. + * Automatically called after any modification. + * + * Maintains the MAX_UNDO_HISTORY limit by removing oldest states + * when necessary. + * + * @return void + * @note Clears redoQueue when new modifications occur + */ void saveState(); - + + /** + * @brief Registers a type T for serialization/deserialization. + * + * Sets up infrastructure for handling type T in the ItemManager: + * - Records type name and type_index + * - Registers serialization schema + * - Registers deserialization function + * + * @tparam T The type to register + * + * @note Must be called before storing items of type T + * @note Called automatically by addItem() if needed + * @see deserializeItemById() + */ template void registerType(); + /** + * @brief Retrieves the compiler-specific type name for type T. + * + * Uses typeid().name() and demangling to get a human-readable name. + * + * @tparam T The type to get the name for + * + * @return std::string The demangled type name (e.g., "int", "MyClass") + * + * @note Static method - can be called without an instance + * @note Platform-dependent implementation + */ template static std::string getCompilerTypeName(); + /** + * @brief Retrieves the JSON schema for a specific type. + * + * Looks up the registered schema function for a type name + * and returns the schema JSON. + * + * @param type The demangled type name + * + * @return json The JSON schema for the type + * @throw Throws if type not registered + * @note Used for validation and documentation + */ json getSchemaForType(std::string type) const; + /** + * @brief Deserializes an item from JSON by its ID. + * + * Reconstructs an item of type T from JSON representation, + * using the item's unique ID. + * + * @tparam T The type of item to deserialize + * @param j The JSON object containing serialized data + * + * @return std::shared_ptr Pointer to deserialized item + * @note Internal method used by import functions + */ template std::shared_ptr deserializeItemById(const json& j); @@ -113,6 +306,15 @@ class ItemManager { public: ItemManager() = default; + + /** + * @brief Destructor - cleans up all stored items and internal structures. + * + * Safely deallocates all items, maps, and queues. Ensures proper + * cleanup during ItemManager shutdown, catching and logging any exceptions. + * + * @note Thread-safe - uses lock_guard during cleanup + */ ~ItemManager() { try { { @@ -131,6 +333,7 @@ class ItemManager { } } + void printId() { std::cout << "\033[1;31m::: Debug: ID: id | Item Tag \033[0m\n" << std::endl; @@ -141,43 +344,165 @@ class ItemManager { void showSignature(); - // Get the compiler type name of a given type T - // This function uses typeid and demangling to get a human-readable type name. + /** + * @brief Retrieves the human-readable type name from a compiler mangled name. + * + * Converts mangled type names (from typeid) into readable format: + * - GCC/Clang: Uses abi::__cxa_demangle() + * - MSVC: Returns type name as-is + * - Other: Returns "Unknown compiler" + * + * @param mangledName The compiler-generated mangled type name + * + * @return std::string The demangled type name + * @note Public utility for name demangling + */ std::string demangleType(const std::string& mangledName) const; - // Display registered deserializers - // This function displays all registered deserializers in the ItemManager. - // It iterates through the deserializers map and prints each type name and its corresponding function. + /** + * @brief Displays all currently registered deserializer functions. + * + * Outputs a list of all types that have deserializers registered, + * useful for debugging serialization setup. + * + * @return void + * @note Primarily for diagnostic/logging purposes + */ void displayRegisteredDeserializers(); - // Check if an item with a specific tag exists + /** + * @brief Checks if an item with the specified tag exists. + * + * @param tag The user-defined tag to search for + * + * @return bool True if an item with this tag exists, false otherwise + * @note O(1) average time complexity + */ bool hasItem(const std::string& tag) const; - // Add item with a specific tag + /** + * @brief Adds a new item with specified tag to the manager. + * + * Stores a shared pointer to an object of type T (wrapped in ItemWrapper) + * and indexes it by the given tag. Automatically registers the type if needed. + * Creates a state snapshot for undo functionality. + * + * @tparam T The type of the object being added + * @param obj Shared pointer to the object of type T + * @param tag User-defined unique identifier for this item + * + * @return void + * @throw Throws if tag already exists + * @note Thread-safe - uses mutex protection + * @note Automatically calls saveState() to enable undo + * @note If T not yet registered, registerType() is called + * @see removeByTag() + */ template void addItem(std::shared_ptr obj, const std::string& tag); - // Modify item using a given modifier function + /** + * @brief Modifies an existing item using a provided function. + * + * Retrieves the item by tag and applies a modifier function to it. + * Useful for atomic updates without fetching and re-storing. + * + * @tparam T The expected type of the item + * @param tag The tag of the item to modify + * @param modifier Function taking T& as parameter and applying changes + * + * @return bool True if modification succeeded, false if item not found or wrong type + * @note Thread-safe + * @note Automatically saves state for undo + * @note Type mismatch silently returns false + */ template bool modifyItem(const std::string& tag, const std::function& modifier); - // Retrieve item by tag + /** + * @brief Retrieves a copy of an item by tag. + * + * Returns a copy (not reference) of the stored item if it exists + * and matches type T. + * + * @tparam T The expected type of the item + * @param tag The tag of the item to retrieve + * + * @return std::optional Contains the item copy if found, std::nullopt otherwise + * @note Returns a copy, not a reference - modifications don't affect stored item + * @note Thread-safe + * @note Type mismatch returns std::nullopt + */ template std::optional getItem(const std::string& tag) const; - // Retrieve raw BaseItem by tag + /** + * @brief Retrieves a mutable reference to an item by tag. + * + * Provides direct mutable access to the stored item for efficient updates. + * + * @tparam T The expected type of the item + * @param tag The tag of the item to retrieve + * + * @return T& Mutable reference to the stored item + * @throw Throws std::runtime_error if tag not found or type mismatch + * @note Changes to reference immediately affect stored state + * @note Thread-safe - though only recommended for single-threaded scenarios + */ template T& getItemRaw(const std::string& tag); - + + /** + * @brief Retrieves a const reference to an item by tag. + * + * Provides direct const (read-only) access to the stored item. + * + * @tparam T The expected type of the item + * @param tag The tag of the item to retrieve + * + * @return const T& Const reference to the stored item + * @throw Throws std::runtime_error if tag not found or type mismatch + * @note Changes cannot be made through this reference + */ template const T& getItemRaw(const std::string& tag) const; - // Display all items + /** + * @brief Displays all items currently stored in the manager. + * + * Iterates through all items and calls display() on each, + * outputting their contents to standard output. + * + * @return void + * @note Format depends on BaseItem::display() implementation in each type + */ void displayAll() const; - + + /** + * @brief Displays a specific item by tag. + * + * Shows the content of a single item identified by tag. + * + * @param tag The tag of the item to display + * + * @return void + * @note Outputs nothing if tag not found + */ void displayByTag(const std::string& tag) const; - // Remove item by tag + /** + * @brief Removes an item by its tag. + * + * Deletes the item with the specified tag from storage. + * Updates all internal indexes (idMap, typeUsage). + * + * @param tag The tag of the item to remove + * + * @return void + * @note Thread-safe + * @note Automatically saves state for undo + * @note Silent if tag not found + */ void removeByTag(const std::string& tag); // Undo and redo state changes @@ -185,123 +510,488 @@ class ItemManager { void redo(); - // void importFromFile(const std::string& filename); + /** + * @brief Imports all items from a JSON file. + * + * Loads a previously exported JSON file containing multiple items + * and adds them to the manager. Items must have unique tags. + * + * @param filename Path to the JSON file to import + * + * @return void + * @throw Throws if file cannot be opened or JSON is malformed + * @note Thread-safe + * @note Clears existing items if needed + * @see exportToFile_Json() + */ void importFromFile_Json(const std::string& filename); - // Asynchronously import items from a JSON file + /** + * @brief Asynchronously imports items from a JSON file. + * + * Launches a background thread to load items from a JSON file, + * allowing the main thread to continue execution. + * + * @param filename Path to the JSON file to import + * + * @return void + * @note Asynchronous - returns immediately + * @note Thread-safe via background thread + * @see importFromFile_Json() + */ void asyncImportFromFile_Json(const std::string& filename); - // Import a single object from a JSON file + /** + * @brief Exports all items to a JSON file. + * + * Serializes all stored items to JSON and writes to file. + * Creates a human-readable JSON representation. + * + * @param filename Path to the output JSON file + * + * @return void + * @throw Throws if file cannot be created/written + * @note Overwrites existing file + * @see importFromFile_Json() + */ void exportToFile_Json(const std::string& filename) const; - // Asynchronously export items to a JSON file + /** + * @brief Asynchronously exports items to a JSON file. + * + * Launches a background thread to export items to JSON format, + * non-blocking operation. + * + * @param filename Path to the output JSON file + * + * @return void + * @note Asynchronous - returns immediately + * @see exportToFile_Json() + */ void asyncExportToFile_Json(const std::string& filename) const; - // Import items from a JSON file + /** + * @brief Imports a single item from a JSON file. + * + * Loads a specific object of given type from a JSON file with specified tag. + * + * @param filename Path to the JSON file + * @param type The demangled type name of the item + * @param tag The tag to assign to the imported item + * + * @return std::shared_ptr Pointer to the imported item + * @throw Throws if file/type/item not found + * @note Adds the item to the manager + */ std::shared_ptr importSingleObject_Json(const std::string& filename, const std::string& type, const std::string& tag); - // Asynchronously import a single object from a JSON file + /** + * @brief Asynchronously imports a single item from JSON. + * + * @param filename Path to the JSON file + * @param typeName The demangled type name + * @param tag The tag for the imported item + * + * @return void + * @note Asynchronous operation + */ void asyncImportSingleObject_Json(const std::string& filename, const std::string& typeName, const std::string& tag); - // Export items to a binary file + /** + * @brief Exports all items to a binary file. + * + * Serializes items in a compact binary format for space-efficient storage. + * + * @param filename Path to the output binary file + * + * @return bool True if export succeeded, false otherwise + * @note Binary format is not human-readable + * @see importFromFile_Binary() + */ bool exportToFile_Binary(const std::string& filename) const; - // Asynchronously export items to a binary file + /** + * @brief Asynchronously exports items to binary format. + * + * @param filename Path to the output binary file + * @return void + * @note Asynchronous operation + */ void asyncExportToFile_Binary(const std::string& filename) const; - // Import items from a binary file + /** + * @brief Imports items from a binary file. + * + * Loads items previously exported in binary format. + * + * @param filename Path to the binary file to import + * + * @return bool True if import succeeded, false otherwise + * @see exportToFile_Binary() + */ bool importFromFile_Binary(const std::string& filename); - // Asynchronously import items from a binary file + /** + * @brief Asynchronously imports items from binary format. + * + * @param filename Path to the binary file + * @return void + * @note Asynchronous operation + */ void asyncImportFromFile_Binary(const std::string& filename); - // Import a single object from a binary file + /** + * @brief Imports a single item from a binary file. + * + * @param filename Path to the binary file + * @param type The demangled type name + * @param tag The tag to assign + * + * @return std::shared_ptr Pointer to imported item + */ std::shared_ptr importSingleObject_Binary(const std::string& filename, const std::string& type, const std::string& tag); - // Asynchronously import a single object from a binary file + /** + * @brief Asynchronously imports a single binary object. + * + * @param filename Path to the binary file + * @param typeName The type name + * @param tag The item tag + * @return void + */ void asyncImportSingleObject_Binary(const std::string& filename, const std::string& typeName, const std::string& tag); - // Export items to an XML file + /** + * @brief Exports items to an XML file. + * + * Serializes items in XML format (requires tinyxml2 library). + * + * @param filename Path to the output XML file + * + * @return bool True if export succeeded, false otherwise + * @note Requires tinyxml2 integration + * @see importFromFile_XML() + */ bool exportToFile_XML(const std::string& filename) const; - // Asynchronously export items to an XML file + /** + * @brief Asynchronously exports items to XML format. + * + * @param filename Path to the output XML file + * @return void + */ void asyncExportToFile_XML(const std::string& filename) const; - // Import items from an XML file + /** + * @brief Imports items from an XML file. + * + * Loads items previously exported in XML format. + * + * @param filename Path to the XML file to import + * + * @return bool True if import succeeded, false otherwise + * @see exportToFile_XML() + */ bool importFromFile_XML(const std::string& filename); - // Asynchronously import items from an XML file + /** + * @brief Asynchronously imports items from XML format. + * + * @param filename Path to the XML file + * @return void + */ void asyncImportFromFile_XML(const std::string& filename); - // Import a single object from an XML file + /** + * @brief Imports a single item from an XML file. + * + * @param filename Path to the XML file + * @param type The demangled type name + * @param tag The item tag + * + * @return std::optional> Item if found, nullopt otherwise + */ std::optional> importSingleObject_XML(const std::string& filename, const std::string& type, const std::string& tag); - // Asynchronously import a single object from an XML file + /** + * @brief Asynchronously imports a single XML object. + * + * @param filename Path to the XML file + * @param type The type name + * @param tag The item tag + * @return void + */ void asyncImportSingleObject_XML(const std::string& filename, const std::string& type, const std::string& tag); - // Export items to a CSV file + /** + * @brief Exports items to a CSV file. + * + * Exports items in comma-separated values format for spreadsheet applications. + * + * @param filename Path to the output CSV file + * + * @return bool True if export succeeded, false otherwise + * @note CSV format suitable for Excel/Calc + * @see importFromFile_CSV() + */ bool exportToFile_CSV(const std::string& filename) const; - // Asynchronously export items to a CSV file + /** + * @brief Asynchronously exports items to CSV format. + * + * @param filename Path to the output CSV file + * @return void + */ void asyncExportToFile_CSV(const std::string& filename) const; - // Import items from a CSV file + /** + * @brief Imports items from a CSV file. + * + * Loads items previously exported in CSV format. + * + * @param filename Path to the CSV file to import + * + * @return bool True if import succeeded, false otherwise + * @see exportToFile_CSV() + */ bool importFromFile_CSV(const std::string& filename); - // Asynchronously import items from a CSV file + /** + * @brief Asynchronously imports items from CSV format. + * + * @param filename Path to the CSV file + * @return void + */ void asyncImportFromFile_CSV(const std::string& filename); - // Import a single object from a CSV file + /** + * @brief Imports a single item from a CSV file. + * + * @param filename Path to the CSV file + * @param type The demangled type name + * @param tag The item tag + * + * @return std::shared_ptr Pointer to imported item + */ std::shared_ptr importSingleObject_CSV(const std::string& filename, const std::string& type, const std::string& tag); - // Asynchronously import a single object from a CSV file + /** + * @brief Asynchronously imports a single CSV object. + * + * @param filename Path to the CSV file + * @param type The type name + * @param tag The item tag + * @return void + */ void asyncImportSingleObject_CSV(const std::string& filename, const std::string& type, const std::string& tag); - // Register a type for serialization and deserialization + /** + * @brief Displays all registered type names. + * + * Lists all types that have been registered for serialization/deserialization. + * + * @return void + * @note Useful for debugging type registry + */ void listRegisteredTypes() const; - //Filter items by tags. + /** + * @brief Filters and displays items matching specified tags. + * + * Shows only items whose tags are in the provided list. + * + * @param tags Vector of tag strings to filter by + * + * @return void + * @note Items not matching any tag are not displayed + */ void filterByTag(const std::vector& tags) const; - // Sort items by tag + /** + * @brief Sorts and displays all items alphabetically by tag. + * + * @return void + * @note Display-only operation, doesn't change storage order + */ void sortItemsByTag() const; - // Display all class names of items + /** + * @brief Displays the class/type name of all stored items. + * + * Outputs a list of type names for each stored item. + * + * @return void + */ void displayAllClasses() const; - // Get the current state of items + /** + * @brief Provides read-only access to the internal items map. + * + * Returns a const reference to the complete items storage. + * Useful for iteration or analysis of all stored items. + * + * @return const std::unordered_map>& Reference to items map + * @note Thread-safe for read-only access + */ const std::unordered_map>& getItemMapStore() const; }; #include "ItemManager.tpp" - +/** + * @class GlobalItemManager + * @brief Singleton pattern implementation providing global access to a single ItemManager instance. + * + * This class enforces the singleton design pattern, ensuring that only one ItemManager exists + * throughout the application's lifetime. It provides thread-safe access to the global item storage. + * + * @details + * **Singleton Pattern Implementation:** + * - Private constructor prevents direct instantiation + * - Copy constructor and assignment operator deleted + * - Static getInstance() method returns the singleton instance + * - Guaranteed initialization via static local variable in getInstance() + * + * **Thread Safety:** + * - Static local variables in getInstance() are thread-safe in C++11 and later + * - Initial construction is guaranteed atomic + * + * **Usage Pattern:** + * ```cpp + * // Access the global ItemManager + * GlobalItemManager& globalMgr = GlobalItemManager::getInstance(); + * ItemManager& mgr = globalMgr.getItemManager(); + * + * // Use the manager normally + * mgr.addItem(myObject, "tag"); + * ``` + * + * **Advantages:** + * - Single point of access across the entire application + * - Guaranteed lifetime spanning entire program execution + * - No manual destruction needed + * - Clean encapsulation of the ItemManager + * + * @note Use GlobalItemManager::getInstance() to access the global ItemManager + * @note Cannot be copied or moved - prevents multiple instances + * @note Call resetItemManager() if you need to reinitialize the manager + * + * @author Victor + * @date 2025 + * @see ItemManager + */ class GlobalItemManager { private: - // Private constructor to prevent direct instantiation + /** + * @brief Private constructor - prevents external instantiation. + * + * Initializes the singleton instance with a new ItemManager. + * Called only once by getInstance(). + */ GlobalItemManager() : itemManager(std::make_unique()) {} - // Pointer to the ItemManager instance + /** + * @brief Unique pointer to the single ItemManager instance. + * + * Managed automatically - destroyed when GlobalItemManager + * is destroyed (at program termination). + */ std::unique_ptr itemManager; public: - // Delete copy constructor and assignment operator to enforce singleton behavior + /** + * @brief Deleted copy constructor - prevents copying singleton instances. + * + * Enforces singleton pattern by preventing creation of duplicate instances. + */ GlobalItemManager(const GlobalItemManager&) = delete; + + /** + * @brief Deleted copy assignment operator - prevents assignment of singleton instances. + * + * Enforces singleton pattern by preventing copying via assignment. + */ GlobalItemManager& operator=(const GlobalItemManager&) = delete; - // Static method to access the singleton instance + /** + * @brief Provides thread-safe access to the singleton instance. + * + * Returns a reference to the global GlobalItemManager instance. + * Creates the instance on first call using static initialization, + * which is thread-safe in C++11 and later. + * + * @return GlobalItemManager& Reference to the singleton instance + * + * @note Thread-safe - guaranteed by C++11 static initialization + * @note First call may have slight overhead due to initialization + * @note All subsequent calls return immediately with cached instance + * + * **Thread Safety Guarantee:** + * C++11 and later guarantee that the static variable initialization + * is performed exactly once, even in multi-threaded environments. + * + * **Usage:** + * ```cpp + * GlobalItemManager& global = GlobalItemManager::getInstance(); + * ItemManager& mgr = global.getItemManager(); + * ``` + */ static GlobalItemManager& getInstance() { static GlobalItemManager instance; // Guaranteed to be initialized once return instance; } - // Public method to access the ItemManager instance + /** + * @brief Provides access to the managed ItemManager instance. + * + * Returns a reference to the internal ItemManager that handles + * all item storage and management operations. + * + * @return ItemManager& Reference to the managed ItemManager + * + * **Usage:** + * ```cpp + * GlobalItemManager& global = GlobalItemManager::getInstance(); + * ItemManager& mgr = global.getItemManager(); + * mgr.addItem(obj, "tag"); + * ``` + * + * @note Always returns the same ItemManager instance + * @note Reference is valid for the entire program lifetime + */ ItemManager& getItemManager() { return *itemManager; } - // Example method to reset the ItemManager instance + /** + * @brief Reinitializes the ItemManager with a fresh instance. + * + * Destroys the current ItemManager and creates a new one. + * Useful for testing or complete reset of item storage. + * + * @return void + * + * @warning This operation: + * - Deletes all items currently in storage + * - Clears undo/redo history + * - Resets type registry + * - Outputs debug message to console + * + * **Use Cases:** + * - Test setup/teardown + * - Factory reset scenarios + * - Memory cleanup in long-running applications + * + * **Example:** + * ```cpp + * GlobalItemManager& global = GlobalItemManager::getInstance(); + * global.resetItemManager(); // Start fresh + * ``` + * + * @note Should only be called when you intend to lose all data + * @note Not thread-safe if called while other threads access the manager + */ void resetItemManager() { itemManager = std::make_unique(); std::cout << "::: Debug: ItemManager instance reset.\n"; From df6d5a16be0ec15aa814422344259201b905c76e Mon Sep 17 00:00:00 2001 From: Moin Shaikh <78309271+Moin2002-tech@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:18:34 +0530 Subject: [PATCH 16/16] Implement JSON serialization type traits and documentation This module provides compile-time type traits for JSON serialization detection and schema management. It includes detection for from_json, to_json, serialize, and schema methods, enabling compile-time validation of serialization capabilities. --- src/utils/Json_traits.hpp | 425 +++++++++++++++++++++++++++++++++++++- 1 file changed, 417 insertions(+), 8 deletions(-) diff --git a/src/utils/Json_traits.hpp b/src/utils/Json_traits.hpp index 1835e44..2d61a3f 100644 --- a/src/utils/Json_traits.hpp +++ b/src/utils/Json_traits.hpp @@ -9,35 +9,327 @@ using json = nlohmann::json; // :: Trait to detect free from_json(json, T&) function // **************************************************** +/** + * @file Json_traits.hpp + * @brief Type trait system for JSON serialization detection and schema management. + * + * @details This module provides compile-time type traits (SFINAE-based) to detect + * and validate JSON serialization capabilities at compile time. It forms the foundation + * of the SmartStore framework's polymorphic serialization system. + * + * **Core Features:** + * - Detection of from_json() free functions via @ref has_from_json + * - Detection of to_json() free functions via @ref has_to_json + * - Detection of serialize() member functions via @ref has_serialize + * - Detection of schema() static methods via @ref has_schema + * + * **Technical Foundation:** + * + * This implementation uses: + * 1. **SFINAE (Substitution Failure Is Not An Error)**: Template substitution failures + * don't cause compilation errors but instead select alternative overloads. + * 2. **Expression SFINAE**: Checks if expressions are valid using decltype() in function return types + * 3. **Integral Constants**: Results compile into std::integral_constant for type-safe boolean traits + * + * **Why Type Traits Matter:** + * + * Without these traits, ItemManager would need to: + * - Store type-specific serializers manually (tedious and error-prone) + * - Write serialization logic multiple times for each type + * - Lose compile-time validation of serialization capability + * + * With traits, we can: + * - Enable/disable features based on type capabilities + * - Generate appropriate code only for serializable types + * - Provide clear compile-time errors if serialization not supported + * - Maintain single source of truth for type requirements + * + * @section CompileTimeVerification + * + * These traits are evaluated at **compile time**, meaning: + * - Zero runtime overhead (traits are erased after compilation) + * - Compile-time errors if type doesn't support required operations + * - Optimization opportunities through partial specialization + * + * **Usage Example:** + * ```cpp + * // In ItemManager, check if type T is serializable: + * if constexpr (has_serialize::value) { + * // Only compiled if T has serialize() method + * } else if constexpr (has_from_json::value) { + * // Only compiled if T has from_json() function + * } + * ``` + * + * @author Victor + * @date 2025 + */ -namespace detail { +/** + * @namespace detail + * @brief Internal namespace for SFINAE trait implementations. + * + * Contains implementation details for type trait detection. Users should not + * directly interact with classes/types in this namespace; use the public + * alias templates instead (@ref has_from_json, @ref has_to_json, etc). + * + * @details Traits in this namespace use advanced C++ metaprogramming: + * + * **SFINAE Mechanism Explained:** + * 1. Two test() functions are defined with identical names + * 2. One accepts int (higher priority in overload resolution) + * 3. Other accepts ... (fallback, lower priority) + * 4. First overload uses decltype() with the expression to check + * 5. If expression is valid -> first overload selected -> returns std::true_type + * 6. If expression is invalid (substitution fails) -> fallback selected -> returns std::false_type + * 7. Result stored in ::value compile-time constant + * + * **Example Signature:** + * ```cpp + * template + * static auto test(int) -> decltype( + * from_json(std::declval(), std::declval()), + * std::true_type{} + * ); + * ``` + * - `std::declval()`: Creates dummy json object without construction + * - `std::declval()`: Creates dummy U reference without construction + * - `decltype(expr)`: Returns the type of the expression + * - Comma operator: Expression sequence returns last element (std::true_type{}) + * + * @note This is an internal implementation detail namespace + */ +namespace detail +{ + /** + * @struct has_from_json_impl + * @brief SFINAE trait implementation for detecting free function from_json(). + * + * @tparam T The type to check for from_json support + * + * **Purpose:** Determines whether a free function `from_json(const json&, T&)` exists. + * + * **Implementation Details:** + * + * Two overloaded test() static functions are defined: + * 1. test(int): Returns std::true_type if `from_json(json, T&)` expression is valid + * 2. test(...): Fallback that returns std::false_type + * + * **How SFINAE Works Here:** + * + * ```cpp + * template + * static auto test(int) -> decltype( + * from_json(std::declval(), std::declval()), + * std::true_type{} + * ); + * ``` + * + * When instantiating test(0): + * - Tries to evaluate: `from_json(json, T&)` + * - If valid expression: Returns std::true_type via comma operator + * - If invalid (no from_json): Substitution fails → tries test(...) instead + * - Result stored in ::value compile-time constant + * + * **Usage in ItemManager:** + * ```cpp + * if constexpr (has_from_json::value) { + * // Can deserialize MyType from JSON + * } + * ``` + * + * @see @ref has_from_json Public alias template + */ template - struct has_from_json_impl { + struct has_from_json_impl + { + /** + * @brief Overload 1: Detects if free function from_json() exists. + * + * This overload is preferred when resolving test(0) because int is a better match + * than the ellipsis (...) in the fallback overload. + * + * @tparam U Type to check (deduced as T) + * @return std::true_type if `from_json(const json&, U&)` is a valid expression + * + * @note If the decltype expression is invalid, this overload is discarded via SFINAE + * and the fallback test(...) is used instead. + */ template static auto test(int) -> decltype(from_json(std::declval(), std::declval()), std::true_type{}); + + /** + * @brief Overload 2: Fallback if from_json not found. + * + * This overload is selected when the first overload's decltype fails. + * Has lower priority due to ellipsis parameter matching. + * + * @return std::false_type indicating from_json() doesn't exist + */ template static std::false_type test(...); + + /** + * @brief Compile-time boolean constant for trait result. + * + * - true if from_json(const json&, T&) is callable + * - false if from_json() does not exist + * + * Evaluation: `decltype(test(0))` produces either std::true_type or std::false_type, + * and `::value` extracts the boolean constant. + */ static constexpr bool value = decltype(test(0))::value; }; + + /** + * @struct has_to_json_impl + * @brief SFINAE trait implementation for detecting free function to_json(). + * + * @tparam T The type to check for to_json support + * + * **Purpose:** Determines whether a free function `to_json(json&, const T&)` exists. + * + * **Serialization Pattern:** + * + * This trait detects the inverse operation of from_json(). While from_json deserializes + * data FROM JSON into a C++ object, to_json serializes FROM a C++ object INTO JSON. + * + * **Expected Function Signature:** + * ```cpp + * void to_json(nlohmann::json& j, const MyType& obj) { + * j = nlohmann::json{{"field1", obj.field1}, {"field2", obj.field2}}; + * } + * ``` + * + * **SFINAE Detection:** + * + * Checks if calling `to_json(json_ref, object_ref)` is valid at compile time. + * - If valid: returns std::true_type + * - If invalid: Substitution fails → fallback returns std::false_type + * + * **Typical Usage in ItemManager:** + * ```cpp + * if constexpr (has_to_json::value) { + * // Can serialize MyType to JSON + * } + * ``` + * + * @see @ref has_to_json Public alias template + */ template - struct has_to_json_impl { + struct has_to_json_impl + { + /** + * @brief Overload 1: Detects if free function to_json() exists. + * + * Checks if `to_json(json&, const T&)` is a callable expression. + * + * @tparam U Type to check (deduced as T) + * @return std::true_type if to_json is callable for type U + */ template static auto test(int) -> decltype(to_json(std::declval(), std::declval()), std::true_type{}); + + /** + * @brief Overload 2: Fallback if to_json not found. + * + * Selected when first overload's SFINAE check fails. + * + * @return std::false_type indicating to_json() doesn't exist + */ template static std::false_type test(...); + /** + * @brief Compile-time boolean constant for to_json availability. + * + * - true if `to_json(json&, const T&)` can be called + * - false otherwise + */ static constexpr bool value = decltype(test(0))::value; }; + + /** + * @struct has_serialize_impl + * @brief SFINAE trait implementation for detecting member function serialize(). + * + * @tparam T The type to check for serialize() member function + * + * **Purpose:** Determines whether a member method `T::serialize() const` exists. + * + * **Member Function Pattern:** + * + * Unlike from_json/to_json which are free functions, serialize() is a member method: + * ```cpp + * class MyItem { + * public: + * json serialize() const { + * return json{{"field1", field1}, {"field2", field2}}; + * } + * private: + * int field1; + * std::string field2; + * }; + * ``` + * + * **SFINAE Detection:** + * + * Checks if calling `obj.serialize()` (const method) is valid at compile time. + * + * **Priority in ItemManager:** + * + * Serialization methods are checked in priority order: + * 1. Does type have serialize() member? → Use it + * 2. Does type have to_json() free function? → Use it + * 3. Is type a BaseItem? → Use BaseItem::serialize() + * 4. Otherwise → ERROR: Type not serializable + * + * **Usage Example:** + * ```cpp + * if constexpr (has_serialize::value) { + * // Can call T::serialize() + * auto json_obj = item.serialize(); + * } + * ``` + * + * @see @ref has_serialize Public alias template + */ template - struct has_serialize_impl { + struct has_serialize_impl + { + /** + * @brief Overload 1: Detects if serialize() member function exists. + * + * Checks if const method `T::serialize()` can be called on object of type T. + * + * @tparam U Type to check (deduced as T) + * @return std::true_type if T::serialize() const is callable + */ template static auto test(int) -> decltype(std::declval().serialize(), std::true_type{}); + + + /** + * @brief Overload 2: Fallback if serialize not found. + * + * Selected when first overload's decltype fails (serialize not found). + * + * @return std::false_type indicating serialize() doesn't exist + */ template static std::false_type test(...); + + /** + * @brief Compile-time boolean constant for serialize() availability. + * + * - true if T has member function `serialize() const` + * - false if serialize() not found + */ static constexpr bool value = decltype(test(0))::value; }; } // Public traits + template using has_from_json = std::integral_constant::value>; @@ -47,17 +339,134 @@ using has_to_json = std::integral_constant::va template using has_serialize = std::integral_constant::value>; -// Trait to check if a type has a static schema() method -namespace detail { + /** + * @struct has_schema_impl + * @brief SFINAE trait implementation for detecting static schema() method. + * + * @tparam T The type to check for schema support + * + * **Purpose:** Determines whether a static method `T::schema()` exists. + * + * **Schema Concept:** + * + * A JSON schema describes the structure and constraints of data. The schema() method + * should return a JSON Schema representation of the type's structure: + * + * ```cpp + * class User { + * public: + * static json schema() { + * return json{ + * {"type", "object"}, + * {"properties", { + * {"name", {{"type", "string"}}}, + * {"age", {{"type", "integer"}}}, + * {"email", {{"type", "string"}, {"format", "email"}}} + * }}, + * {"required", {"name", "age"}} + * }; + * } + * private: + * std::string name; + * int age; + * std::string email; + * }; + * ``` + * + * **SFINAE Detection:** + * + * Checks if the static method `T::schema()` can be called at compile time. + * This is a static method (not instance method), so detection differs from serialize(). + * + * **Use Cases for Schema:** + * + * 1. **Validation**: Check if JSON data matches expected structure before deserialization + * 2. **Documentation**: Provide machine-readable type documentation + * 3. **UI Generation**: Automatically generate forms from schema + * 4. **Interoperability**: Share structure information with other systems + * + * **Usage in ItemManager:** + * ```cpp + * if constexpr (has_schema::value) { + * // Can retrieve schema for validation + * auto type_schema = T::schema(); + * } + * ``` + * + * @see @ref has_schema Public alias template + */ +namespace detail +{ + /** + * @brief Overload 1: Detects if static schema() exists. + * + * Returns std::true_type if T::schema() is valid expression. + * The trailing comma operator returns second operand (std::true_type{}). + * + * **Comma Operator Explanation:** + * + * In C++, the comma operator evaluates both expressions and returns the second: + * ```cpp + * (expr1, expr2) // evaluates expr1, discards result, returns expr2 + * ``` + * + * Usage here: + * ```cpp + * decltype(U::schema(), std::true_type{}) + * // Evaluates U::schema() for validity + * // Returns std::true_type{} as the decltype result + * // If U::schema() is invalid → substitution fails → SFINAE fallback used + * ``` + * + * @tparam U Type parameter to check + * @return std::true_type if T::schema() valid expression + */ template - struct has_schema_impl { + struct has_schema_impl + { + /** + * @brief Overload 1: Detects if static schema() exists. + * + * Returns std::true_type if T::schema() is valid expression. + * The trailing comma operator returns second operand (std::true_type{}). + * + * **Comma Operator Explanation:** + * + * In C++, the comma operator evaluates both expressions and returns the second: + * ```cpp + * (expr1, expr2) // evaluates expr1, discards result, returns expr2 + * ``` + * + * Usage here: + * ```cpp + * decltype(U::schema(), std::true_type{}) + * // Evaluates U::schema() for validity + * // Returns std::true_type{} as the decltype result + * // If U::schema() is invalid → substitution fails → SFINAE fallback used + * ``` + * + * @tparam U Type parameter to check + * @return std::true_type if T::schema() valid expression + */ template static auto test(int) -> decltype(U::schema(), std::true_type{}); + + /** + * @brief Overload 2: Fallback if schema not found. + * + * Returns std::false_type when U::schema() substitution fails. + * This overload is selected when the primary template can't be instantiated. + */ template static std::false_type test(...); + /** + * @brief Compile-time boolean for schema availability. + * + * true if static schema() exists, false otherwise. + */ static constexpr bool value = decltype(test(0))::value; }; } template -using has_schema = std::integral_constant::value>; \ No newline at end of file +using has_schema = std::integral_constant::value>;