-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Segment class non-copyable and move-only #4580
base: main
Are you sure you want to change the base?
Conversation
Squashed commit of the following: commit c4b4bf0145bacad4e75d1c5321bf4a5da26b981c Author: Joachim Dick <[email protected]> Date: Sat Mar 1 18:43:47 2025 +0100 Avoid unnecessary copy of SegmentSettings Based on a comment from blazoncek in this PR: wled#4578 (comment) commit 74d83d87d8f302f1c264eded65b919838165ebb0 Author: Joachim Dick <[email protected]> Date: Sat Mar 1 18:20:17 2025 +0100 On Segment-move-only: Replace some _segments.push_back calls with _segments.emplace_back calls Originally from TripleWhy: TripleWhy@8efc2dd commit d8131e4c017f8d01f0f20b94c33f0f7d163afb94 Author: Joachim Dick <[email protected]> Date: Sat Mar 1 17:27:35 2025 +0100 Make Segment class non-copyable and move-only Co-Authored-By: TripleWhy <[email protected]> Co-Authored-By: Blaž Kristan <[email protected]>
WalkthroughThe changes introduce a new structure ( Changes
✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
wled00/led.cpp (1)
30-32
: Use a narrower type forfirstSel
if suitable.
IfgetFirstSelectedSegId()
returns a smaller type, using a dedicated type (e.g.,uint8_t
) can enhance clarity and ensure the variable’s range is consistent. Also consider verifying thatfirstSel
is valid if no segment is selected.wled00/FX_fcn.cpp (1)
1055-1070
: Consider adding comparisons forcheck1
,check2
, andcheck3
.
The comment at line 1070 hints at these fields possibly affecting segment behavior. If they do, leaving them out could mask meaningful changes.wled00/FX.h (1)
790-830
: NewSegmentSettings
struct
A helpful encapsulation of segment properties. The comment at line 825 hints at missingcheck1/check2/check3
. If you want complete parity withSegment
, consider adding them.Would you like me to generate a patch integrating these flags here, or open a new issue to track it?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
wled00/FX.h
(9 hunks)wled00/FX_fcn.cpp
(4 hunks)wled00/json.cpp
(1 hunks)wled00/led.cpp
(1 hunks)
🔇 Additional comments (10)
wled00/json.cpp (1)
36-36
: EnsureSegmentSettings
covers all needed fields.
Switching from copying the entireSegment
toSegmentSettings
is more efficient and aligns with the move-only approach. However, check if additional fields (e.g.,check1/check2/check3
) also need to be included to track their changes properly.wled00/FX_fcn.cpp (1)
1758-1762
: Check zero-dimension scenarios in matrix vs. strip mode.
WhenisMatrix
is true, the new code usesSegment::maxWidth
andSegment::maxHeight
for the default segment. If these values are zero or invalid, it might create an invalid segment. Consider adding validation or fallback logic.wled00/FX.h (8)
19-19
: Include<array>
looks good
This inclusion is necessary to usestd::array
for strongly typed arrays.
389-390
: Forward declaration ofSegmentSettings
Helps keep dependencies ordered and avoids circular inclusion issues.
420-420
: Use ofstd::array
forcolors
Replacing the C-style array withstd::array
is a solid move toward improved type safety and modern C++ best practices.
571-571
: Deleted copy constructor forSegment
This aligns with the move-only design strategy, preventing accidental copies.
586-586
: Deleted copy assignment operator forSegment
Ensures thatSegment
cannot be copied, reinforcing move-only semantics.
846-849
: Move-only semantics forWS2812FX
Deleting the copy constructor and assignment operator while defaulting the move counterparts solidifies move-only usage for this class.
935-935
: SwitchedappendSegment
to take an rvalue reference
Accommodates move semantics, avoiding unnecessary copies ofSegment
objects.
632-632
:❓ Verification inconclusive
differs
now expectsSegmentSettings
Be sure to update all call sites passing aSegment
so they construct or have aSegmentSettings
argument.Please run the following script to detect any remaining references that still use the old signature:
🏁 Script executed:
#!/bin/bash # Searching for code references to differs(Segment rg "differs\s*\(\s*Segment\b" -A 2Length of output: 36
Action: Verify All 'differs' Call Sites Now Use 'SegmentSettings'
The recent grep search did not return any instances where a
Segment
is passed todiffers
. This suggests that the conversion to requiring aSegmentSettings
argument has been applied everywhere. However, because the automated search produced no output (which might also indicate that some edge cases were missed), please perform a manual review of the call sites fordiffers
to ensure thorough coverage.
- Verify that no call sites are inadvertently still passing a
Segment
.- Confirm that all invocations build or supply a proper
SegmentSettings
argument.
Hmmm With this current proposal, note, that The reason I did not forbid copies however is that it currently looks like it's going to be needed again... Which is also why I didn't bother avoiding memcpy, which is used internally by the copy constructor (which doesn't make it less dangerous, just less obvious). |
Oh right, what I forgot to mention is that I also only call differs if that would actually do anything, i.e. if |
Indeed it may be. |
@JoaDick what was the driving force for removing/prohibiting segment copy? BTW I'm working on a feature that depends on segment copy (or at least segment settings + effect data + pixel state data copy). |
Checked the code: It isn't. |
I found the reason:
Ergo: |
Fully agree, and I'd appreciate such a refactoring in the future. For this PR I resisted to do so to keep it less intrusive.
As suggested by @blazoncek,
Thanks for this hint; I wasn't aware of that change and will have a closer look at it. |
The main reason is probably very similar to what @TripleWhy is striving for: providing a possibility that effects can be implemented in terms of a class and not just as a plain function. The current way stems back from very early roots - Kitesurfer's WS2812FX - and is still suffering from the same limitations: keeping data persistent between frames is only possible through this kind of a workaround via In my playground, I tried something very similar (but much smaller) as @TripleWhy's proposal from here: #4549 Another driving force are SW design considerations. In a nutshell, only so called value-classes shall be copyable. Most other kind of classes - in particular logic-classes like
Thanks a lot for this hint - I'll have a closer look at that as well. Hopefully we can figure out an idea to bring all that great stuff together! |
In my understanding that would alter the current behavior in funny ways. Either that, or I'm missing something |
Ah, now I think I understand what you mean. In deed this |
Whoa, that's quite a lot and really impressive! As a quick solution, I'd suggest to replace the copy constructor with a |
Yes. Just FYI the current implementation in that branch is working miraculously on ESP8266 yielding 55 FPS on a 16x16 matrix. 40+ FPS when 3 effects are stacked one atop another and blended. AFAIK @DedeHai needed to implement (complex) memory management since PS needs a lot of memory and running i.e. 3 layers (with transition going on) exhausted available RAM. As for suggestion: I am still a newbie as far as C++ goes. I may not understand everything you or @TripleWhy are trying to pass on to me. |
Well, you know what would solve all of that, right? ;)
Yeah I thought it could be that way, too. But transitions from old um, states, to new states don't just include the effect and all of its parameters, but also the way you set up segments. These days I don't use segments much, but before 2D setups, you had to define separate presets with completely different segment definitions to achieve a simple color transition from left to right or top to bottom. The transition should still work when switching between those setups. Thus all old and new segment definitions and effect parameters need to be known during the transition. |
@blazoncek - OK, might have been a bit too much information at once in my last comments... What I wanted to say with those many words:
|
This would surely be a first step in the right direction 👍
Very well summarized. I think we're having quite the same thoughts on this topic. Now it would be really good to get feedback if there is general interest in approaching this direction towards class based effects. |
I've read that again. Hopefully getting it right. The main issue is that Rewriting everything from scratch (this or the other PR) aside the easy "solution" is to prohibit memory allocation from within objects placed into This, of course, contradicts PS approach which does need extra memory and has to come with its own solution. IMO the solution lies somewhere in the middle. The approach by @TripleWhy is very radical (and probably unfit for every existing effect, especially PS system). If the effect functions are converted into instantiated objects then 2 things are missing: object creation and destruction. When do we create effect object? At JSON parsing time, when effect ID actually change, or later when strip is being servidced? The same goes with destruction. However, when there are transitions active both effects should be running so we cannot destroy previous effect when selecting a new one. I have mentioned somewhere that current/legacy approach to effects has an issue at the time effect no longer runs. There is no notification to effect function to cleanup after itself (destructor). Currently the All that being said I am not against the change, however, WLED is a very complex system so any change has to be thought over several times. |
Uh... This is the disabled Segment copy PR, I don't think we should go into detail about Effects here, let's head over there if you want to discuss it. However before you copy the comment over there, I'd appreciate if you'd simply take a look at the PR |
Exactly, this is a big drawback.
I'd not prohibit that at once, but provide alternatives to be able to move away from it step by step.
It is essential to remain compatible. Breaking changes that require adjusting "everything" are a no-go. Of course that might make some parts tricky...
Exactly, these triggers are something I was looking for a long time ago. And gave up back then...
This would surely be helpful. But IMHO rather an optimization, and not vital from the very start.
That's why I try to keep changes small and less intrusive. Better several small changes one after another than on big bang. |
@TripleWhy I can see your point so I'll cut that short: As I'm working on a feature that requires segment copy, this PR does not get a green light from me. Others may vote otherwise though. IMO there should be a new PR that deals with informing effect function when it is first executed and when it is last executed, prior to any other change. The best place to start that hunt is in |
Ack, that's fine for me. Looking forward to seeing your layering branch on main! |
This is really pure coincidence... Apparently @TripleWhy and me have been working on the same topic at the same time:
Eliminating copies of the Segment class. See the first PR here: #4578
The reasons for this alternative PR - after hesitating for a long time - are the following:
The last point of this list is the most important one IMHO. Using
memcpy()
,reinterpret_cast()
, and alike on objects seems very convenient in the beginning. But they are also very prone to becoming a really bad pitfall in the future! And the performance gain - if any - is very disputable.That is also why many C++ coding standards from the industry (MISRA, AUTOSAR, SEI-CERT ...) do not allow using these; see here for example:
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-memset
https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP57-CPP.+Prefer+special+member+functions+and+overloaded+operators+to+C+Standard+Library+functions
As said, I was hesitating for a long time, and studied the PR and its discussion intensively. Just wait for the other PR to become available? Or incorporate its suggestions into this C++ compliant PR and give credit?
Well... here's the result. I really hope no one is offended by this alternative!
Summary by CodeRabbit
New Features
Refactor
Chores