Conversation
Replaced sequential std::vector::erase calls inside while loops with the erase-remove idiom using std::remove_if in `ApplicationWidget::updateWindows` and `ApplicationWidget::removeAllToasts`. This changes the complexity of the operation from O(N^2) to O(N). Co-authored-by: segin <480709+segin@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Pull request overview
Optimizes window/toast removal in the UI root widget by replacing repeated std::vector::erase() in loops with the erase–remove idiom to reduce worst-case complexity when removing many elements on the UI thread.
Changes:
- Refactors
ApplicationWidget::updateWindows()to remove dismissable toasts viastd::remove_if+erase(). - Refactors
ApplicationWidget::removeAllToasts()to remove all toast windows viastd::remove_if+erase(). - Adds an
<algorithm>include to support the new use ofstd::remove_if.
Comments suppressed due to low confidence (1)
src/widget/ui/ApplicationWidget.cpp:25
- The file-level copyright/licensing block is no longer the first thing in the file because
<algorithm>was inserted above it. This breaks the consistent pattern in othersrc/widget/ui/*.cppfiles (header comment first, then includes) and can cause automated license/header scanners to miss it. Also,psymp3.halready includes<algorithm>(include/psymp3.h:95), so this include can likely be removed entirely (or at least moved below the header comment / after#include "psymp3.h").
#include <algorithm>
/*
* ApplicationWidget.cpp - Implementation for root application widget
* This file is part of PsyMP3.
* Copyright © 2025-2026 Kirn Gill <segin2005@gmail.com>
*
* PsyMP3 is free software. You may redistribute and/or modify it under
* the terms of the ISC License <https://opensource.org/licenses/ISC>
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that
* the above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "psymp3.h"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Code Review
This pull request refactors the toast removal logic in ApplicationWidget.cpp by replacing manual loops with the std::remove_if and erase idiom. Feedback highlights that the new include is misplaced before the file header and warns that the predicate in updateWindows contains side effects which could lead to undefined behavior if the container is modified during iteration.
| @@ -1,3 +1,4 @@ | |||
| #include <algorithm> | |||
There was a problem hiding this comment.
The #include <algorithm> directive is placed at the very top of the file, before the license and header comments. This is non-standard and breaks the file's structure. Additionally, <algorithm> is already included in the main project header psymp3.h, which this file likely includes. If you want to follow the 'include what you use' principle, please move this include to the section where other system headers are located, typically after the file header and the main project include.
/*
* ApplicationWidget.cpp - Implementation for root application widget
* This file is part of PsyMP3.
* ...
*/
#include "psymp3.h"
#include <algorithm>| m_windows.erase( | ||
| std::remove_if(m_windows.begin(), m_windows.end(), [](const std::unique_ptr<Widget>& w) { | ||
| if (auto* toast = dynamic_cast<ToastWidget*>(w.get())) { | ||
| if (toast->shouldDismiss()) { | ||
| toast->dismiss(); // Call dismiss callback | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| ++it; | ||
| } | ||
| return false; | ||
| }), | ||
| m_windows.end() | ||
| ); |
There was a problem hiding this comment.
Using std::remove_if with a predicate that has side effects (calling toast->dismiss()) is generally discouraged. While std::remove_if is required to perform exactly dismiss() callback triggers any logic that modifies the m_windows vector (e.g., via removeChild), it will cause iterator invalidation and undefined behavior within std::remove_if.
A safer approach that maintains dismiss() never modifies the container, this optimization is valid for performance.
💡 What: Replaced the sequential
std::vector::erasecalls inside awhileloop with the erase-remove idiom usingstd::remove_ifandm_windows.erase()inApplicationWidget::updateWindowsandApplicationWidget::removeAllToasts.🎯 Why: Calling
erase()in the middle of astd::vectorinside a loop requires shifting all subsequent elements each time, leading to O(N^2) complexity. The erase-remove idiom processes the vector in a single pass, moving non-erased elements forward without repeated shifting, achieving O(N) complexity. This prevents the UI thread from blocking when removing multiple toasts or windows.📊 Measured Improvement:
A focused benchmark was created to test erasing elements based on a condition matching the
shouldDismiss()logic on a 10,000-element vector ofunique_ptr<Widget>.Note: The full
make checksuite was run, but hit pre-existing failures in theOggSeekingEnginecode. However,make -C src/widget/uiconfirmedApplicationWidget.cppcompiles correctly without errors.PR created automatically by Jules for task 12698717499449677933 started by @segin