Compile time generated tables
Asked Answered
P

1

2

Thanks to some trickery I'm able to generate a table at compile time, the values in the table are not very useful though. For example a table 5x5 looks like this:

1,2,3,4,5,
1,2,3,4,5,
1,2,3,4,5,
1,2,3,4,5,
1,2,3,4,5,

Where the comma's are for clarity purpose. The code which create this table is the following:

#include <iostream>

using ll = long long;

template<typename type,type...data>
struct sequence
{
    static type seq_data[sizeof...(data)];
    static const ll size;
    type operator[](ll index){
        return seq_data[size-index-1];
    }
};

template<typename type,type...data>
type sequence<type,data...>::seq_data[sizeof...(data)] = { data... };
template<typename type,type...data>
const ll sequence<type,data...>::size{sizeof...(data)};

template<ll n,ll l,ll...data> struct create_row
{
    typedef typename create_row<n-1,l+1,l,data...>::value value;
};
template<ll l,ll...data>
struct create_row<0,l,data...>
{
    typedef sequence<ll,data...> value;
};

template<ll cols,ll rows>
struct table
{
    typename create_row<cols,1>::value row_data[rows];
    static const ll size;
};

template<ll cols,ll rows>
const ll table<cols,rows>::size{cols*rows};

using namespace std;

int main()
{
    table<5,5> my_table;
    for(int i{0};i<5;i++)
    {
        for(int j{0};j<5;j++)
        {
            cout<<my_table.row_data[i][j]<<",";
        }
        cout<<endl;
    }
}

As you can see to create the single row's I'm forced to use the hard coded value '1' in the struct table, for this reason create_table will always return the same sequence, a series of number from 1 to n. For this reason each row in the table have the same values.

What's I would like to do is to encode at compile time a different starting value for each row, in order to have a table which looks like this:

1,2,3,4,5,
6,7,8,9,10,
11 <CUT>

I wasn't able to find any way to create a table of such a kind.

Did you have any idea on how to do that?

Perennial answered 11/2, 2016 at 8:58 Comment(5)
Are you trying to generate the given row's data each time one indexes into it? Are you looking to allocate memory for each entry in the table (as in a std::array? Or is "faking" the table good enough (like this)?Weighbridge
Unfortunately you solution is not good for me,I need the real table. The code I've posted is a simplification for the problem of building the pascal triangle by metaprogramming means. You may consider it more an exercise than something of any real use. I believe that it may be done even though at the moment I don't have a working implementation.Perennial
The main issue with your approach is that you're attempting to generate the portion of the table at the point that the user indexes into it. If the table is generated entirely at compile time, which it can be, then indexing into said table should involve indexing into actual memory; at run-time we must already know the type we are indexing into. That is, the result of operation[] has to be a known type. A sequence or integer_sequence, etc. encodes the array into the type; there's no actual memory involved. If we want to perform some kind of recursion with variadic templatesWeighbridge
(continued from above), then it must happen at compile time. So, would it be okay if the result of your compile-time table was a std::array<std::array<T, COLS>, ROWS>?Weighbridge
I believe it should be ok, my point now is proving that it can be done, what I care is that the table is build at compile time. AFAIK once the table is build up, the values create at compile time are going to be stored at runtime in memory, when creating the fibonacci sequence for example, I can access the elements of the sequence at runtime by plain indexing, in the ASM disassembly is also visible the section where the list of integers is stored. If you have any idea on how to proceed with std::array then please, it will be very instructive at least.Perennial
V
4

I'm not absolutely sure at the end your post whether you are interested in:-

  • Generating a matrix that is compiletime populated with successive values of some function f(i) that wrap-around the matrix in row-first order, e.g.
    Cols = 3; Rows = 3; f(i) = 2i; Vals = (1,2,3,4,5,6,7,8,9) ->

    |02|04|06|
    ----------
    |08|10|12|
    ----------
    |14|16|18|

or:-

  • Generating a matrix that in which successive rows are compiletime populated with successive values of some function f(i) for some specified initial i per row, e.g.
    Cols = 3; f(i) = 3i; First_Vals = (4,7,10) -> 
    |12|15|18|
    ----------
    |21|24|27|
    ----------
    |30|33|36|

Anyhow there are ways to do both, and here is one you can use with a C++14 conforming compiler. (As @AndyG has commented, the appropriate implementation for a compiletime matrix - leveraging the Standard Library - is an std::array of std::array.)

#include <array>
#include <utility>

namespace detail {

template<typename IntType, IntType(*Step)(IntType), IntType Start, IntType ...Is> 
constexpr auto make_integer_array(std::integer_sequence<IntType,Is...>)
{
    return std::array<IntType,sizeof...(Is)>{{Step(Start + Is)...}};
}

template<typename IntType, IntType(*Step)(IntType), IntType Start, std::size_t Length> 
constexpr auto make_integer_array()
{
    return make_integer_array<IntType,Step,Start>(
        std::make_integer_sequence<IntType,Length>());
}


template<
    typename IntType, std::size_t Cols, 
    IntType(*Step)(IntType),IntType Start, std::size_t ...Rs
> 
constexpr auto make_integer_matrix(std::index_sequence<Rs...>)
{
    return std::array<std::array<IntType,Cols>,sizeof...(Rs)> 
        {{make_integer_array<IntType,Step,Start + (Rs * Cols),Cols>()...}};
}

} // namespace detail

/*
    Return a compiletime initialized matrix (`std::array` of std::array`)
    of `Cols` columns by `Rows` rows. Ascending elements from [0,0] 
    in row-first order are populated with successive values of the
    constexpr function `IntType Step(IntType i)` for `i` in
    `[Start + 0,Start + (Rows * Cols))` 
*/
template<
    typename IntType, std::size_t Cols, std::size_t Rows, 
    IntType(*Step)(IntType), IntType Start
> 
constexpr auto make_integer_matrix()
{
    return detail::make_integer_matrix<IntType,Cols,Step,Start>(
        std::make_index_sequence<Rows>());
}

/*
    Return a compiletime initialized matrix (`std::array` of std::array`)
    of `Cols` columns by `sizeof...(Starts)` rows. Successive rows are populated
    with successive values of the constexpr function `IntType Step(IntType i)` 
    for `i` in `[start + 0,start + Cols)`, for `start` successively in `...Starts`.  
*/
template<typename IntType, std::size_t Cols, IntType(*Step)(IntType), IntType ...Starts> 
constexpr auto make_integer_matrix()
{
    return std::array<std::array<IntType,Cols>,sizeof...(Starts)> 
        {{detail::make_integer_array<IntType,Step,Starts,Cols>()...}};
}

You can make a demo program by appending:

#include <iostream>

using namespace std;

template<typename IntType>
constexpr auto times_3(IntType i)
{
    return i * 3;
}

static constexpr auto m4x6 = make_integer_matrix<int,4,6,&times_3<int>,4>();
static constexpr auto m5x1 = make_integer_matrix<int,5,&times_3<int>,7>();
static constexpr auto m6x5 = make_integer_matrix<int,6,&times_3<int>,11,13,17,19,23>();
static_assert(m4x6[0][0] == 12,"");

int main()
{
    cout << "A 4 x 6 matrix that wraps around in steps of `3i` from `i` = 4" << endl; 
    for (auto const & ar  : m4x6) {
        for (auto const & i : ar) {
            cout << i << ' ';
        }
        cout << endl;
    }
    cout << endl;
    cout << "A 6 x 5 matrix with rows of `3i` for initial `i` in <11,13,17,19,23>" 
        << endl;
    for (auto const & ar  : m6x5) {
        for (auto const & i : ar) {
            cout << i << ' ';
        }
        cout << endl;
    }
    cout << endl;
    cout << "A 5 x 1 matrix with rows of of ` 3i` for initial `i` in <7>" 
        << endl;
    for (auto const & ar  : m5x1) {
        for (auto const & i : ar) {
            cout << i << ' ';
        }
        cout << endl;
    }

    return 0;
}

which should output:

A 4 x 6 matrix that wraps around in steps of `3i` from `i` = 4
12 15 18 21 
24 27 30 33 
36 39 42 45 
48 51 54 57 
60 63 66 69 
72 75 78 81 

A 6 x 5 matrix with rows of `3i` for initial `i` in <11,13,17,19,23>
33 36 39 42 45 48 
39 42 45 48 51 54 
51 54 57 60 63 66 
57 60 63 66 69 72 
69 72 75 78 81 84 

A 5 x 1 matrix with rows of of ` 3i` for initial `i` in <7>
21 24 27 30 33

See it at ideone

You might also be interested in std::experimental::make_array

Vengeful answered 13/2, 2016 at 11:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.