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

How do you statically generate float data at compile time?

本文关键字:数据 静态 编译      更新时间:2023-10-16

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

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 预编译脚本来生成包含过滤器数据的源文件,但我不想在这里使用它,因为那是它自己的鱼壶!

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

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

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

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

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

    如果模板静态相互依赖,它们就会出现问题 - 在您的情况下,它们不是,每个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;
}

程序集(叮当声):

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

如果你把someDistribution改成一个函数,即使是内联函数,你会看到计算必须进行。

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

您可以使用可变参数模板获得拼图的一部分。一旦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 版本:

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