使用c ++的余弦查找表

时间:2017-07-24 13:44:04

标签: c++ c++11 templates lookup-tables

这是一个片段,应该生成2048个元素的余弦查找表,取自Changyi Gu的“Building Embedded Systems”一书:

#include <cmath>
#include <array>

template<typename T>
constexpr T look_up_table_elem (int i) {
    return {};
}

template<>
constexpr uint16_t look_up_table_elem (int i) {
    return round (cos (static_cast <long double>(i) / 2048 * 3.14159 / 4) * 32767);
}


template<typename T, int... N>
struct lookup_table_expand{};

template<typename T, int... N>
struct lookup_table_expand<T, 1, N...> {
    static constexpr std::array<T, sizeof...(N) + 1> values = {{ look_up_table_elem<T>(0), N... }};
};

template<typename T, int L, int... N> 
struct lookup_table_expand<T, L, N...>: lookup_table_expand<T, L-1, look_up_table_elem<T>(L-1), N...> {};


template<typename T, int... N>
constexpr std::array<T, sizeof...(N) + 1> lookup_table_expand<T, 1, N...>::values;

const std::array<uint16_t, 2048> lookup_table = lookup_table_expand<uint16_t, 2048>::values;

注意:为C ++ 11编写。

我来自一个主要的Java世界,我已经掌握了C ++的基础知识。由于书中从未真正解释过,我真的很困惑这个片段如何实现这个任务,以及它如何实现以下目标(也从书中取出):

  

模板特化和类继承将帮助我们摆脱constexpr函数只能将返回状态作为其函数体的限制,这就是为什么在表大小增加时必须手动填充查找表的原因。

非常感谢任何帮助。我理解使用constexpr模板生成实际值的部分,但我真的不确定结构正在做什么以及如何构造最终数组。

1 个答案:

答案 0 :(得分:8)

首先让我们来看看以下一行:

const std::array<uint16_t, 2048> lookup_table =
        lookup_table_expand<uint16_t, 2048>::values;

lookup_table将从values结构中保存的lookup_table_expand<uint16_t, 2048>数组进行复制构造。这很简单,现在让我们看看模板实例化完成后会发生什么。

我们有一个空主体的主模板(前向声明就足够了,我们不会在这种形式下使用它):

template<typename T, int... N>
struct lookup_table_expand {
};

lookup_table_expand<uint16_t, 2048>将匹配以下partial specialization主模板:

template<typename T, int L, int... N> 
struct lookup_table_expand<T, L, N...> :
        lookup_table_expand<T, L - 1, look_up_table_elem<T>(L - 1), N...> {
};

因为继承上面的模板将使用不断增长的模板parameter list进行递归实例化,直到当前模板与主模板的以下部分特化不匹配为止:

template<typename T, int... N>
struct lookup_table_expand<T, 1, N...> {
    static constexpr std::array<T, sizeof...(N) + 1> values = {{
        look_up_table_elem<T>(0), N...
    }};
};

L在递归中变为1时,将发生与上述模板的匹配。此时,模板参数列表(N...)将包含以下函数的调用结果,值为1到2047:

constexpr uint16_t look_up_table_elem(int i) {
    return round(cos(static_cast<long double>(i) / 2048 * 3.14159 / 4) * 32767);
}

这是lookup_table_expand模板(values)的唯一成员将使用模板参数列表的值进行初始化。

请注意,valuesstatic constexpr数据成员,可以在class / struct声明中进行初始化,因此以下行甚至不需要:

template<typename T, int... N>
constexpr std::array<T, sizeof...(N) + 1> lookup_table_expand<T, 1, N...>::values;

values数组将由lookup_table_expand<uint16_t, 2048>继承,因此最后您可以从该结构中访问它。