Yes, this should be possible in principle. Here's a bit to get you started:
template <class T, std::size_t... Dim>
using VariableLengthTable = std::tuple<std::array<T, Dim>...>;
This is a tuple of std::arrays, each with a length specified by one of the template arguments.
Note that because the length of a std::array is part of its type, the first dimension cannot be an array, since its members would need different types. However, std::tuple works nicely. As long as you're willing to use std::get<i> instead of [i], and restrict yourself to compile-time is, you should be good.
If a compile-time i is not enough, you have two options:
One, use VariableLengthTable as above and add a runtime-to-compiletime conversion. Conceptually, something like this switch:
T& get(std::size_t x, std::size_t y)
{
switch (x) {
case 0: return std::get<0>(varLenTable)[y];
case 1: return std::get<1>(varLenTable)[y];
case 2: return std::get<2>(varLenTable)[y];
// and so on
}
}
In reality, you'd probably need to compose this using recursion or inheritance to avoid a compile-time out-of-bounds access. Boost.Preprocessor might help with that.
Two, store all the data in one sequential buffer and index into it at runtime. Something like this:
template <class T, std::size_t... Dim>
class VariableLengthTable
{
static const std::array<std::size_t, sizeof...(Dim)> dims = {{ Dim... }};
static const std::array<std::size_t, sizeof...(Dim)> cumulDims = [](){
std::array<std::size_t, sizeof...(Dim)> result;
result[0] = 0;
for (std::size_t idx = 1; idx < sizeof...(Dim); ++idx) {
result[idx] = result[idx - 1] + dims[idx];
}
return result;
}();
std::array<T, cumulDims[sizeof...(Dim) - 1] + dims[sizeof...(Dim) - 1]> data;
public:
T& get(std::size_t x, std::size_t y)
{
return data[cumulDims[x] + y];
}
};
The code above is to show the principle, not guaranteed to compile as-is.
std::vector<std::vector<int>>or at leaststd::array<std::vector<int>,N>an option?std::vectoris unfortunately too big for our application.