c++templatesvisual-c++template-meta-programminglookup-tables

How to allow higher template recursion limit in MSVC?


I'm writing template metaprogramming code in C++ to generate a look-up table for embedded programming purposes (lack of FPU). I've been using MSVC for prototyping.

When trying to generate the final 128x128, 2 bytes per cell LUT, I'm getting error C1202: recursive type or function dependency context too complex.

But for 100x100, it gets generated in seconds and all is well. I haven't been able to find how to increase the template recursion limit in MSVC on the web.

According to this question from 2014 asking the same thing, Visual C++ - Set the depth of template instantiation, there was none, but I hope things have changed since and someone here may know about it.

Here is the code for context:

const size_t tableSize = 128;
const float unit = 80.;
using Cell = std::pair<uint8_t, uint8_t>;

constexpr auto f = [](uint8_t x, uint8_t y) -> Cell {
    float _x = x / unit;
    float _y = y / unit;

    float norm2 = std::sqrt(_x * _x + _y * _y);
    float norm2Min1 = norm2 < 1 ? 1 : norm2;
    return {(uint8_t)(x / norm2Min1), (uint8_t)(y / norm2Min1) };
};

template<uint8_t rowNo, uint8_t columnNo> struct Row {
    Cell cell = f(columnNo, rowNo);
    Row<rowNo, columnNo + 1> rest;
};
template<int rowNo> struct Row<rowNo, tableSize-1> {
    Cell cell = f(tableSize-1, rowNo);
};

template<int rowNo> struct Table {
    Row<rowNo, 0> row;
    Table<rowNo + 1> rest;
};
template<> struct Table<tableSize-1> {
    Row<tableSize-1, 0> row;
};

struct LUT {
    Table<0> table;

    Cell precomputedF(uint8_t x, uint8_t y) {
        return ((Cell*)(&table))[y * tableSize + x];
    }
};

int main()
{
    LUT lut;
    int size = sizeof(LUT);

    std::cout << size << "\n\n";

    for (int x = 0; x < tableSize; x++) {
        for (int y = 0; y < tableSize; y++) {
            auto result = lut.precomputedF(x, y);
            std::cout << "(" << (int)result.first << "," << (int)result.second << ")" << " ";
        }
        std::cout << "\n";
    }
}

Solution

  • Don't use templates. Use constexpr

    using Table = std::array<std::array<Cell, tableSize>, tableSize>;
    consteval Table make_LUT() {
        Table table;
        for(size_t x=0; x<tableSize; x++) {
            for(size_t y=0; y<tableSize; y++)
                table[y][x] = f(x,y);
        }
        return table;
    };
    constexpr Table LUT = make_LUT();
    constexpr Cell precomputedF(uint8_t x, uint8_t y) {
        return LUT[y][x];
    }
    

    http://coliru.stacked-crooked.com/a/0d406313f9bec451


    If you really want to stick with something closer to your original code, you can simply use std::make_integer_sequence<uint8_t,tableSize>() to eliminate the recursion in the templates. This has a max depth of 2, instead of 2xtableSize. I haven't quite worked out the syntax to create the table with a max depth of 1 yet, but I suspect it's possible.

    
    template<uint8_t row, std::uint8_t... columns>
    constexpr TableRow makeTableRow(std::integer_sequence<uint8_t,columns...>) {
      TableRow t = {{f(columns,row)...}};
      return t;
    };
    template<std::uint8_t tableSize, std::uint8_t... rows>
    constexpr Table makeTable(std::integer_sequence<uint8_t,rows...>) {
      Table t = {{makeTableRow<rows>(std::make_integer_sequence<uint8_t,tableSize>())...}};
      return t;
    };
    constexpr Table lut = makeTable<tableSize>(std::make_integer_sequence<uint8_t,tableSize>());
    

    http://coliru.stacked-crooked.com/a/345718b3818c2231