Compile time generated tables

江枫思渺然 提交于 2020-01-15 03:15:09

问题


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?


回答1:


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



来源:https://stackoverflow.com/questions/35334752/compile-time-generated-tables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!