代码之家  ›  专栏  ›  技术社区  ›  geoff3jones

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

  •  6
  • geoff3jones  · 技术社区  · 12 年前

    考虑到我想对一些数据进行过滤,我如何避免在运行时生成这些数据,同时保持更改这些过滤器的大小和数据分布的灵活性,同时保持干净、可重复使用的代码。我知道我可以使用模板来做以下事情:

    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 成员被实例化,这样我就可以使用begin和end函数遍历静态“数组”。

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

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

    2 回复  |  直到 12 年前
        1
  •  2
  •   gwiazdorrr    12 年前

    既然你不能使用 contexpr …关于您的问题:

    1. 除非你正在寻找下一个最大的素数,否则你不应该为在启动时进行一次简单的计算而烦恼。试着测量它,你很可能会发现初始化在不到毫秒的时间内完成。

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

      在带来不便之前,一定要先测量一下。

    2. 再说一遍,你为什么在乎?如果 Filter 特殊类型 x 没有在代码中的任何地方使用,为什么值应该在某个地方?

      如果模板静态相互依赖,那么它们就会有问题——在您的情况下,它们并不相互依赖 f 是自治的。

    话虽如此,一个很好的修补工具是 http://gcc.godbolt.org/ -您可以在键入时看到程序集。它不支持MS编译器,但它可以让您很好地猜测编译器是如何优化内容的。

    如果您的分配足够简单,可以成为一个宏,那么它将是一个编译时常数:

    #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;
    }
    

    装配(Clang):

    main:                                   # @main
        movl    $50000, %eax            # imm = 0xC350
        ret
    

    如果你改变 someDistribution 要成为一个函数,即使是内联函数,也必须进行计算。

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

        2
  •  -1
  •   mattnewport    12 年前

    你可以使用可变模板得到拼图的一部分。一旦向标准库添加了对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;
    }
    

    为了在编译时而不是在启动时初始化,尽管您确实需要VS2013所没有的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;
    }