ProcessorChain Iteration? (newb)

Total newb here to both Juce and C++, so be gentle, please…

I have a ProcessorChain declared as such:

using Filter = juce::dsp::IIR::Filter<float>;
    using FreqFilter = juce::dsp::ProcessorChain<Filter, Filter, Filter, Filter, Filter>;
    using MonoChain = juce::dsp::ProcessorChain<FreqFilter, FreqFilter, FreqFilter, FreqFilter>;
    MonoChain leftChain, rightChain;

I’m trying to set the coefficients for all of those Filters in a nested loop:

for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            // ...set up coefficients in here...
             *leftChain.get<i>().get<j>().coefficients = *leftCoefficients;
             *rightChain.get<i>().get<j>().coefficients = *rightCoefficients;
        }
      
    }

The only way I can avoid a compilation error is to replace i and j with hard coded integers.

  1. Why? I know there’s something C+±oriented that I just don’t understand, but I’m having a hard time finding the answer.
  2. Is there a way to do this dynamically?

Any direction anybody can provide would be much appreciated.

I tried to replicate your code and fix it with no success.

I think this question may have the answers, and explained in a pretty newb-friendly way (I know because I could understand them, unlike some other resources I found…):

Tldr; you can’t use runtime variables as template arguments, because templates expand code at compile-time.

Aha! That [sorta] makes sense. I’m so used to writing code in dynamic languages that I have a hard time even figuring out what to Google when I get to something like this. Thanks for pointing me in the right direction!

1 Like

Like answered before, the template argument needs to be declared constexpr or be a constant.

As a quick hack you can wrap it in a switch case statement. It defeats the purpose of the variadic template, but will compile and run.

If you want to learn how it can be used, have a look into the juce::dsp::ProcessorChain class. That one uses compile time loops which can be heavily optimised by the compiler.

To be perfectly honest, I cannot write such code. But thanks to block based processing you can get very far without.

and used here:

1 Like

Hey! There is a rather straightforward way to generate compile time assignments to tuple-like structures up to a constant highest index using templates and if constexpr. Here is an example to illustrate the idea. Not trying to win a code style beauty contest, but maybe it helps:

#include <tuple>

struct Coefficients {};

std::tuple<Coefficients*, Coefficients*, Coefficients*> someTuple;

template <int highestIndex>
void assignToIdx( Coefficients* coeffs )
{
    std::get<highestIndex>(someTuple) = coeffs;
    if constexpr (highestIndex > 0)
    {
        assignToIdx<highestIndex - 1>(coeffs);
    }
}

int main() {
    Coefficients coeffs;
    assignToIdx<2>(&coeffs);

    /* this will effectively expand to:
     std::get<2>(someTuple) = coeffs;
     std::get<1>(someTuple) = coeffs;
     std::get<0>(someTuple) = coeffs;
     */
}

So, while we can’t have compile time evaluated for loops (yet), we can use recursion for the same purpose.

2 Likes