Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template instantiation depth exceeds maximum - how to stop particular case?

I have a following function:

template<unsigned fromLine, unsigned toLine = fromLine>
void stateChanged()
{
    // onStateChangeHandler[fromLine]();

    if (fromLine < toLine)
        stateChanged<fromLine + 1, toLine>();
}

which I call it in a following way:

stateChanged<0>();
stateChanged<1>();
stateChanged<2>();
stateChanged<3>();
stateChanged<4>();
stateChanged<5, 9>();
stateChanged<10, 15>();

I am receiving fatal error: template instantiation depth exceeds maximum of 900. I assumed in C++14 the if condition will stop it automatically. So, how to do this properly?

like image 457
no one special Avatar asked Oct 22 '25 13:10

no one special


2 Answers

Using if constexpr instead of if will prevent this code from infinitely recurring.

As your code is written now, every instantiation of stateChanged requests a different instantiation of stateChanged.

before [Compiler Explorer]

after [Compiler Explorer]

template<unsigned fromLine, unsigned toLine = fromLine>
void stateChanged()
{
    // onStateChangeHandler[fromLine]();

    if constexpr(fromLine < toLine)
    {
        // If the above expression is false, this code is not compiled.
        stateChanged<fromLine + 1, toLine>();
    }
}

Update:

On C++14, the same effect can be produced with some SFINAE.

template<unsigned fromLine, unsigned toLine = fromLine,
         typename std::enable_if_t< fromLine>=toLine >* = nullptr >
void stateChanged()
{
    // onStateChangeHandler[fromLine]();
}

template<unsigned fromLine, unsigned toLine = fromLine,
         typename std::enable_if_t< fromLine<toLine >* = nullptr >
void stateChanged()
{
    // onStateChangeHandler[fromLine]();

    stateChanged<fromLine + 1, toLine>();
}

C++14 after [Compiler Explorer]

like image 184
Drew Dormann Avatar answered Oct 25 '25 03:10

Drew Dormann


Without the ability to use if constexpr in the template definition, you need to change how you do the looping in order to introduce a way for the compiler to reach a terminal state and stop the template instantiation.

We can do this with a helper class that, rather than going from start to finish, processes a number of lines.

template <unsigned curLine, unsigned numLines>
void updateStateChanged()
{
    // onStateChangeHandler[curLine]();
    stateChanged<curLine + 1, numLines - 1>();
}

template <unsigned curLine>
void updateStateChanged<curLine, 0>()
{
    // onStateChangeHandler[curLine]();
}

// Then we can modify your original class to call this helper:

template<unsigned fromLine, unsigned toLine = fromLine>
void stateChanged()
{
    updateStateChanged(fromLine, toLine - fromLine);
}
like image 22
1201ProgramAlarm Avatar answered Oct 25 '25 02:10

1201ProgramAlarm