模板类的静态成员数组的延迟初始化 [英] Lazy initialization of a static member array of a template class

查看:194
本文介绍了模板类的静态成员数组的延迟初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写代码,以执行高斯积分 n 点,其中 n 是编译时常数。



对于给定的 n ,我知道如何计算横坐标和权重。计算必须从头开始为每个不同的 n



现在,我做这些行:

  //这样的几个结构体(laguerre,chebyshev等)。 
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;
}

如下使用:

  double i = gauss_quadrature< legendre< 12> (F); 

现在,我可以专门为翻译单位 legendre< 12> ; ,通过执行

 模板< 
const legendre< 12> :: x [12] = {...};

模板<>
const legendre< 12> :: w [12] = {...};

,一切都很好,只要我只使用12分高斯 - >

现在,我正在尝试不同数量的点,我知道如何生成权重和节点。我可以例如提供一个例程

  void compute_legendre_coeffs(size_t n,double * w,double * x) 

和:




  • 当我调用 gauss_quadrature< legendre< n>< / code>时,模板 legendre< n>

  • legendre< n> 在某些编译时实例化 n ,我希望上面的 compute_legendre_coeffs 在main之前的某个点被调用,以便它填充 如何实现这一点?

$ b
$ b

我知道必须先定义数组:

  template< size_t n> 
const double legendre< n> :: x [n] = {};

template< size_t n>
const double legendre< n> :: w [n] = {};

但我不能想出一个方法来初始化它们。

解决方案

  template< size_t n> 
class legendre
{
public:
static const size_t size = n;
static const double(& getX())[n] {
init();
return x;
}
static const double(& getW())[n] {
init();
return x;
}
private:
static double x [n];
static double w [n];
static void init(){
static bool _ = do_init(x,y);
}
static bool do_init(double * x,double * y){
//在这里进行计算,使用local vars x,y
return true;
}
};
template< size_t n>
double legendre< n> :: x [n];
template< size_t n>
double legendre< n> :: w [n];

通过提供访问者,您可以获得对类的入口点的控制。访问器分派到 init 函数,该函数使用局部静态变量的初始化在程序生命周期中调用 do_init 一次。 do_init 会对成员进行实际初始化。



注意:



根据编译器,这可能不是线程安全的(即不是所有的C ++ 03编译器提供线程安全的静态变量初始化,反过来意味着 do_init 可能被并行调用多次,这取决于可能或不是问题的算法 - 如果 do_init 计算值放在一边,只是写入它们,潜在的竞争条件是不相关的,因为净结果将是相同的)。一些编译器提供机制来保证一个执行(我相信boost有这样的机制)。或者,根据您的域,您可以在启动线程之前 填充系数。



实际的数组不能是 const 在这种情况下,因为初始化需要在创建后发生。这不应该是除了可能的微优化之外的任何问题(即,编译器不知道系数的值,所以它不能在编译时执行子表达式求值)。


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 template legendre<n> is automatically instantiated (this is the case).
  • When legendre<n> is instantiated for some compile-time n, I'd like the above compute_legendre_coeffs to be called at some point before main so that it fills the x and w member 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 ?

解决方案

template <size_t n>
class legendre
{
public:
    static const size_t size = n;
    static const double (&getX())[n] {
       init();
       return x;
    }
    static const double (&getW())[n] {
       init();
       return x;
    }
private:
    static double x[n];
    static double w[n];
    static void init() {
       static bool _ = do_init(x,y);
    }
    static bool do_init( double *x, double *y ) {
       // do the computation here, use local vars x, y
       return true;
    }
};
template <size_t n>
double legendre<n>::x[n];
template <size_t n>
double legendre<n>::w[n];

By providing an accessor you gain control of the entry point to your class. The accessors dispatch to a init function that uses initialization of a local static variable to call do_init only once in the program lifetime. The do_init does 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_init might be called more than once in parallel, depending on the algorithm that might or not be an issue --i.e. if do_init computes 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 const in 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).

这篇关于模板类的静态成员数组的延迟初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆