I am writing code to perform Gaussian integration with n points, where n is a compile time constant.
For a given n, I know how to compute abscissas and weights. The computation has to be done from scratch for each different n.
Now, I do something along these lines:
// Several structs like this one (laguerre, chebyshev, etc).
template <size_t n>
struct legendre
{
static const size_t size = n;
static const double x[n];
static const double w[n];
};
template <typename Rule, typename F>
double gauss_quadrature (F&& f)
{
double acc = 0;
for (size_t j = 0; j < Rule::size; j++)
acc += Rule::w[j] * f (Rule::x[j]);
return acc;
}
to be used like this:
double i = gauss_quadrature<legendre<12>> (f);
Now, I can specialize in a translation unit the coefficients for legendre<12>, by doing
template <>
const legendre<12>::x[12] = { ... };
template <>
const legendre<12>::w[12] = { ... };
and everything is fine, as long as I only use 12-points Gauss-Legendre.
Now, I’m experimenting with different number of points, and I know how to generate the weights and nodes. I can for instance provide a routine
void compute_legendre_coeffs (size_t n, double* w, double* x);
and :
- When I call
gauss_quadrature<legendre<n>>, the templatelegendre<n>is automatically instantiated (this is the case). - When
legendre<n>is instantiated for some compile-timen, I’d like the abovecompute_legendre_coeffsto be called at some point before main so that it fills thexandwmember arrays. How do I achieve this ?
I know must define the arrays first:
template <size_t n>
const double legendre<n>::x[n] = {};
template <size_t n>
const double legendre<n>::w[n] = {};
but I can’t come up with a method to initialize them. Anyone has a trick to do so ?
By providing an accessor you gain control of the entry point to your class. The accessors dispatch to a
initfunction that uses initialization of a local static variable to calldo_initonly once in the program lifetime. Thedo_initdoes the actual initialization of the members.Notes:
Depending on the compiler, this might not be thread safe (i.e. not all C++03 compilers provide thread safe initialization of static variables, which in turn means that the
do_initmight be called more than once in parallel, depending on the algorithm that might or not be an issue –i.e. ifdo_initcomputes the values aside and just writes them, the potential race condition is irrelevant as the net result will be the same). Some compilers offer mechanisms to guarantee one off execution (I believe boost has such a mechanism). Alternatively depending on your domain, you might be able to prime the coefficients before you start the threads.The actual arrays cannot be
constin this case, as the initialization needs to happen after they are created. That should not be an issue for anything other than possible micro optimizations (i.e. the compiler does not know about the values of the coefficients, so it cannot perform sub-expression evaluation at compile time).