#pragma once #include #include #include #include #include // Nothrow versions of std::make_unique. Return nullptr on allocation failure // instead of calling abort() (the default when exceptions are disabled on ESP32). // // Single object: // auto obj = makeUniqueNoThrow(); // if (!obj) { LOG_ERR("TAG", "OOM"); return false; } // // Array: // auto buf = makeUniqueNoThrow(size); // if (!buf) { LOG_ERR("TAG", "OOM"); return false; } // buf[0] = 0xFF; // someApi(buf.get(), size); // template requires(!std::is_array_v) std::unique_ptr makeUniqueNoThrow(Args&&... args) { return std::unique_ptr(new (std::nothrow) T(std::forward(args)...)); } template requires std::is_unbounded_array_v std::unique_ptr makeUniqueNoThrow(size_t count) { using Elem = std::remove_extent_t; return std::unique_ptr(new (std::nothrow) Elem[count]()); } // Helper struct to call a cleanup function on exit from any scope. // Use with a lambda to avoid unnecessary allocations from std::function/std::bind: // Example: // auto jpeg = makeUniqueNoThrow(); // ScopedCleanup cleanup{[&jpeg]{ jpeg->close(); }}; // template struct [[nodiscard]] ScopedCleanup final { const F fn; explicit ScopedCleanup(F f) : fn{std::move(f)} {} ScopedCleanup(const ScopedCleanup&) = delete; ScopedCleanup& operator=(const ScopedCleanup&) = delete; ScopedCleanup(ScopedCleanup&&) = delete; ScopedCleanup& operator=(ScopedCleanup&&) = delete; ~ScopedCleanup() { fn(); } }; template ScopedCleanup(F) -> ScopedCleanup;