如何在编译时静态生成浮点数据?

时间:2013-11-01 15:33:01

标签: c++ visual-studio-2013 compile-time data-generation

鉴于我想对某些数据执行过滤,如何避免在运行时生成此数据,但保持更改这些过滤器的大小和数据分布的灵活性,同时保持良好的清洁可重用代码。我知道我可以使用模板执行以下操作:

template <int x> class Filter
{
  static const float f;
  static const Filter<x-1> next;
  inline float* begin(const Filter<x>& el){ return  &f;      }
  inline float* end(const Filter<x>& el)  { return (&f)+x+1; }
};
template <> class Filter<0>
{
  static const float f;
  inline float* begin(const Filter<0>& el){ return  &f;    }
  inline float* end(const Filter<0>& el)  { return (&f)+1; }
};

template <int x> const float Filter<x>::f = someDistribution(x);
template <>      const float Filter<0>::f = someDistribution(0);

根据someDistribution(...),这确实会根据过滤器对象中的索引x在我的过滤器中生成数据。但是我的用途有一些缺点......

1)我认为我说的是,虽然这个数据不是在对象构造上生成的,但是在程序启动时会生成一次。 - 我可以忍受,但我宁愿过滤器在comiletime计算并在当时和那里烘烤(这对于浮点数据是否可能?)

2)过滤器不会实例化“下一个”成员,除非有一个遍历结构长度的成员函数(称为某处!),即

// inside template<int x> class Filter
inline void instantiate() const { next.instantiate(); };
// then inside template<> class Filter<0>
inline void instantiate() const { };

我必须做错了才需要插入实例化函数,这就失败了易于维护的条款。

编辑:我在这里关心的原因是我想确保实例化next成员,这样我就可以使用开始和结束函数遍历静态'数组'。

首先,我如何解决问题2并取消实例化函数,其次是可以修复问题1,以便在编译时动态生成此数据并备份。

(注意我在类似的问题上我使用python预编译脚本生成包含过滤数据的源文件,但我不想在这里使用它,因为那是它自己的鱼!)

2 个答案:

答案 0 :(得分:2)

由于您无法使用contexpr ...关于您的问题:

  1. 除非您正在搜索下一个最大的素数,否则您不应该在启动时进行简单计算。尝试测量它,你很可能发现初始化在不到一毫秒的时间内完成。

    也就是说,在启动时计算的值表现为变量(每次使用时它们的值必须为asked),而编译时常量的值始终是已知的。因此前者可能有点慢,但可能没有任何意义。

    在引入不便之前,请先测量。

  2. 再次,你为什么关心?如果代码中的任何位置没有使用特定Filter的{​​{1}}类型,为什么值应该在某处?

    如果模板静态相互依赖是有问题的 - 在您的情况下它们没有,每个x都是自主的。

  3. 说了这么多,一个很好的修补工具是http://gcc.godbolt.org/ - 你可以在输入时看到装配。它不支持MS编译器,但它可以让你很好地猜测编译器如何优化它们。

    如果你的disrtibution很简单,那么它将是一个编译时常量:

    f

    集会(Clang):

    #define someDistribution(x) x * x
    
    template <int x> struct Filter
    {
      static const float f;
    };
    
    template <int x> const float Filter<x>::f = someDistribution(x);
    
    int main()
    {
      return Filter<200>::f + Filter<100>::f;
    }
    

    如果您将main: # @main movl $50000, %eax # imm = 0xC350 ret 更改为函数,即使是内联函数,您也会看到必须进行计算。

    编辑:记住,你可以用宏做maaany,包括为某些值“专门化”它们。简单的分发应该是对预处理器友好的。

答案 1 :(得分:-1)

您可以使用可变参数模板获取拼图的一部分。一旦将integer_sequence支持添加到标准库,您就可以使用它而不是seq / gen_seq。

#include <array>
#include <iostream>

using namespace std;

template<size_t... Is> struct seq {};
template<size_t N, size_t... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <size_t... Is> struct gen_seq<0, Is...> : seq<Is...>{};

template<typename Func, size_t... Is>
const array<float, sizeof...(Is)>& make_coeffs(Func f, seq<Is...>) {
    static const auto coeffs = array<float, sizeof...(Is)>{ f(Is)... };
    return coeffs;
}

float square(float x) { return x * x; }

int main() {
    const auto coeffs = make_coeffs(square, gen_seq<10>{});
    for (float x : coeffs) {
        cout << x << " ";
    }
    cout << endl;
}

为了使这个编译时间而不是在启动时初始化,虽然你真的想要VS 2013没有的constexpr支持。这是constexpr version

#include <array>
#include <iostream>

using namespace std;

template<size_t... Is> struct seq {};
template<size_t N, size_t... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <size_t... Is> struct gen_seq<0, Is...> : seq<Is...>{};

template<typename Func, size_t... Is>
constexpr array<float, sizeof...(Is)> make_coeffs(Func f, seq<Is...>) {
    return array<float, sizeof...(Is)>{ f(Is)... };
}

constexpr float square(float x) { return x * x; }

int main() {
    constexpr auto coeffs = make_coeffs(square, gen_seq<10>{});
    static_assert(coeffs[3] == 9, "");
    for (float x : coeffs) {
        cout << x << " ";
    }
    cout << endl;
}