Skip to content

Templates - Migrate macros to templates, simplify effects table, add effectId() to all derived effects#748

Merged
davepl merged 10 commits into
mainfrom
templates
Aug 21, 2025
Merged

Templates - Migrate macros to templates, simplify effects table, add effectId() to all derived effects#748
davepl merged 10 commits into
mainfrom
templates

Conversation

@davepl

@davepl davepl commented Aug 18, 2025

Copy link
Copy Markdown
Contributor

Migrate macros to templates, simplify effects table, add effectId() to all derived effects

  • I read the contribution guidelines in CONTRIBUTING.md.
  • I understand the BlinkenPerBit metric, and maximized it in this PR.
  • I selected main as the target branch.
  • All code herein is subjected to the license terms in COPYING.txt.

@davepl
davepl requested a review from rbergen August 18, 2025 23:52

@robertlipe robertlipe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just really hard to review because I think it includes pieces of two other sweeping changes (new audio math, new config system) and there are changes unrelated to the goal mixed in. There are thus some entire files I just didn't look at in this PR.

For what I think is the focal point, I like the direction, but I'd like to peel some of the preprocessing away even more. While the templates are thick, the call sites and the flexibility offered in configurations are pretty nice.

It's a good step forward. This PR specifically is messy, but once I ignore the construction mess, I like the direction.

Comment thread include/effects/matrix/PatternSMRadialFire.h
Comment thread include/effects/matrix/spectrumeffects.h
Comment thread include/effectsupport.h

// Adds a default and JSON effect factory, but the default effect will be disabled upon creation.
template<typename TEffect, typename... Args>
inline EffectFactories::NumberedFactory& AddEffectDisabled(EffectFactories& factories, Args&&... args)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional food for thought: Is is really worth having separate AddEffectDisabled()s for all of these? Should there perhaps be an additional argument that defaults to kEnabled?

Alternately, use the Named Paramater Idiom so you could invoke AddEffect(blah, arg1, arg2, arg3).disabled()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea. I'll defer to Rutger on that!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short answer: no. Particularly with the Disabled() decorator that one would use with RegisterAll(), the separate Add...Disabled() functions are unnecessary.

In my effectid-template branch, I've already removed them.

Comment thread include/globals.h
Comment thread include/globals.h
Comment thread include/soundanalyzer.h
Comment thread platformio.ini
Comment thread include/types.h
Comment thread src/deviceconfig.cpp
Comment thread src/effects.cpp
@rbergen rbergen mentioned this pull request Aug 19, 2025
4 tasks

@rbergen rbergen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As (partially) mentioned already in an out-of-band exchange, I think the switch to a template-based approach is an improvement.

I'm approving with some important side notes, and I do think we should make some further changes after this PR is merged.

Specifically:

  • I did not review changes that are part of PR #746 - I've commented on those changes there (the empty soundanalyzer.cpp probably being the one exception...)
  • There is a number of effects that do not yet adopt the new approach - I think all those that are not currently pulled into an effect set in effects.cpp. I think we should apply this pattern comprehensively if we choose to apply it.
  • There are quite a few whitespace-glitches in the modified effects code (Copilot somehow always seems to struggle with this, even when specifically instructed to fix it.) I didn't comment on these; a comprehensive white-spacing clean-up is part of the branch I will mention in my next point.
  • The per-effect declaration and definition of the effect-specific kId and effectId() members causes a lot of repetitive code, which relies on developer discipline and is prone to copy/paste errors. I've created a effectid-template branch in my fork that proposes a template-based approach for setting an effect's EffectId, which I think is cleaner.
  • With the increased flexibility in effect set compositions, I think the simple approach of an integer version number is no longer the way to distinguish between a persisted effect set and the "standard" set. I've created a factories_hash branch in my fork to propose a hash-based approach instead.

Both branches I just mentioned can be turned into PRs independently after this PR has been merged; I'll happily take care of any merge conflicts that arise if we do indeed adopt both of them - I'm pretty sure there would be some in effectsupport.h.

class PatternAlienText : public LEDStripEffect
{
public:
static constexpr EffectId kId = idMatrixAlienText;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is a matter of preference and opinions vary, but I'd prefer kId to be called Id, or something else without the k - adding it seems like a disclosure of a class-internal implementation decision to me.

This obviously applies to all other effects as well.

{
public:
PatternRainbowFlag() : LEDStripEffect(EFFECT_MATRIX_RAINBOW_FLAG, "RainbowFlag")
PatternRainbowFlag() : LEDStripEffect(idMatrixRainbowFlag, "RainbowFlag")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kId/effectId() combo is missing here, which you get away with because the effect is currently not included in any effect set.

public:

PatternPulse() : LEDStripEffect(EFFECT_MATRIX_PULSE, "Pulse")
PatternPulse() : LEDStripEffect(idMatrixPulse, "Pulse")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing kId/effectId() combo.

public:

PatternQR() : LEDStripEffect(EFFECT_MATRIX_QR, "QR")
PatternQR() : LEDStripEffect(idMatrixQR, "QR")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing kId/effectId() block.


public:
PatternSpiro() : LEDStripEffect(EFFECT_MATRIX_SPIRO, "Spiro")
PatternSpiro() : LEDStripEffect(idMatrixSpiro, "Spiro")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kId/effectId() pair is missing.

Comment thread include/effectsupport.h

// Adds a default and JSON effect factory, but the default effect will be disabled upon creation.
template<typename TEffect, typename... Args>
inline EffectFactories::NumberedFactory& AddEffectDisabled(EffectFactories& factories, Args&&... args)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short answer: no. Particularly with the Disabled() decorator that one would use with RegisterAll(), the separate Add...Disabled() functions are unnecessary.

In my effectid-template branch, I've already removed them.

Comment thread include/ledstripeffect.h
return false;
}

// Helper functions for known setting types, as defined in SettingSpec::SettingType

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robertlipe assuming you're talking about std::variant, maybe. With all the testing of which reference is held by the variant and having to align that with the template parameters for the types that are supported, I'm not exactly sure how much cleaner, or clearer, the code would be.

And we're not talking about a dozen overloads, but seven. :)

Comment thread src/soundanalyzer.cpp Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants