initializing constant arrays

I have made some code to initialize a set of arrays for lookup tables that looks something like this.

1
2
3
4
5
6
7
8
9
10
  double t [i_size] = {. . .}
  double r [i_size][j_size] = {. . .}
  for (ii=0; ii<i_size; ii++) 
  {
    for (jj=0; jj<j_size; jj++) 
    {
      p[ii][jj] = p_calc(t[ii][jj], r[ii][jj]);
      h[ii][jj] = h_calc(t[ii][jj], r[ii][jj]);
    }
  }


These values are constant, so I only need to run them once, and will be referenced in loops potentially thousands of times. Is there a proper way to make these global const (or something similar) once the calculated arrays have been initialized in the loops above?
Is there a proper way to make these global const?

Sure. You can create a short program whose output is a table initializer. I expect this will be the simplest way. I don't know about "proper".

Alternatively, using the indices trick:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# include <iostream>
# include <array>
# include <iomanip>

static constexpr int n_rows = 3, n_cols = 2;

static constexpr double p_calc(double a, double b) { return a + b; }

static constexpr double t [n_rows]         = {0, 10, 20};
static constexpr double r [n_rows][n_cols] = {{0, 1}, {3, 4}, {6, 7}};
  
namespace detail {
    // use std::array or a structure
    template <std::size_t Row, std::size_t... Cols>
    static constexpr std::array<double, n_cols>
    gen_row(std::index_sequence<Cols...>) {
      // compute the aggregate object at p[row]
      return { p_calc(t[Row], r[Row][Cols])... };
    }
    
    
    template <std::size_t... Rows>
    static constexpr std::array<std::array<double, n_cols>, n_rows>
    gen_table(std::index_sequence<Rows...>) {
      return { gen_row<Rows>(std::make_index_sequence<n_cols>())... };
    }
} 

static constexpr std::array<std::array<double, n_cols>, n_rows>
gen_table() { return detail::gen_table(std::make_index_sequence<n_rows>()); }

static constexpr auto table = gen_table(); 
int main() {
    for (auto const& row: table) {
        for (auto const& col: row)
            std::cout << std::setw(2) << col << " ";
        std::cout << '\n';
    }
}
Live demo:
http://coliru.stacked-crooked.com/a/47a932273c64da4e

Edit: I'm worried I've made assumptions I perhaps shouldn't have.
You should only be doing such a thing if the computation is too heavy to perform at runtime. Usually you should just initialize your table in the obvious way.

If you're concerned about the public name being const-qualified, you can use a computed result from a function or lambda to initialize it. Note that this precludes the use of C-style arrays because of array-decay; std::array<> doesn't have this problem.
1
2
3
4
5
static const std::array<double, 3> foo = 
  [](){ std::array<double, 3> result; 
         /* modify result here */  
         return result; 
  }(); // call immediately  
Last edited on
Something like this, perhaps:

header:
1
2
3
4
5
constexpr std::size_t NROWS = 100 ;
constexpr std::size_t NCOLS = 200 ;
using lookup_table_type = double[NROWS][NCOLS] ;

const lookup_table_type& lookup_table() ;


typical usage:
1
2
3
4
5
6
void foo()
{
    const lookup_table_type& table = lookup_table() ;

    // use table
}


implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// initialise the table, return reference to initialised table
static const lookup_table_type& init() // called only once, before the table is used for the first time
{
    static lookup_table_type table{} ;

    // initialize the lookup table

    return table ;
}

const lookup_table_type& lookup_table()
{
    static const lookup_table_type& table = init() ; // reference initialisation is done only once
                                            // even if called simultaneously from multiple threads
                                            
    return table ; // return reference to (the same) initialised table
}
Topic archived. No new replies allowed.