我目前正在尝试创建一个名为MakeByte
的可变模板函数,该函数将接受任意数量的位参数,并将它们放入一个字节。以下是它的用法示例:
// The Wii U has 4 RAM chips, here we select a seemingly "random" one using an
// algorithm to generate one from the coordinates.
quint32 bank_bit_0 = Bit((y / (16 * m_num_pipes)) ^ x_3);
quint32 bank_bit_1 = Bit((y / (8 * m_num_pipes)) ^ x_4);
quint32 bank = MakeByte(bank_bit_0, bank_bit_1);
我有三个功能,涉及单独的标题:
template <typename T1, typename... T2> T1 MakeByte(T1 bit, T2... bits)
,调用递归函数的外部代码将使用的函数。template <typename T1, typename... T2> T1 MakeByte(T1 byte, quint32 pos, T1 bit, T2... bits)
,迭代每个位的递归函数。该函数有额外的参数来跟踪最后一个字节,以及当前位置以放入下一位。template <typename T1, typename T2> T1 MakeByte(T1 byte, quint32 pos, T2 bit)
,处理最后一位的功能。以下是完整的3个定义:
template <typename T1, typename T2>
constexpr T1 MakeByte(T1 byte, quint32 pos, T2 bit)
{
return byte | (bit << pos);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 byte, quint32 pos, T1 bit, T2... bits)
{
return MakeByte(byte | (bit << pos), pos + 1, bit, bits...);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
return MakeByte(static_cast<T1>(0), 0, bit, bits...);
}
问题是,在使用g ++编译时,我收到此错误:
/home/kyle/Documents/Projects/C++/Qt/MK8Studio/Source/Common.h:44: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
return MakeByte(static_cast<T1>(0), 0, bit, bits...);
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
此时,我重命名了两个递归函数,以防有歧义:
template <typename T1, typename T2>
constexpr T1 MakeByte_(T1 byte, quint32 pos, T2 bit)
{
return byte | (bit << pos);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte_(T1 byte, quint32 pos, T1 bit, T2... bits)
{
return MakeByte_(byte | (bit << pos), pos + 1, bit, bits...);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
return MakeByte_(static_cast<T1>(0), 0, bit, bits...);
}
这段代码确实编译了,但我不禁觉得这有点像黑客。从设计的角度来看,在可变模板函数中减少模糊性的最佳方法是什么?
答案 0 :(得分:1)
也许这是个人品味的问题,但我更愿意避免让两个版本的函数做同样的事情(在这种情况下:shift和or。)。
我建议写一个只返回一个值的终端案例(没有班次,没有或者)
template <typename T1>
constexpr T1 MakeByte_ (T1 byte, quint32)
{ return byte; }
和递归案例
template <typename T1, typename... T2>
constexpr T1 MakeByte_(T1 byte, quint32 pos, T1 bit, T2... bits)
{ return MakeByte_(byte | (bit << pos), pos + 1, bit, bits...); }
这避免了歧义,因为如果MakeByte_
接收到三个或更多个参数,则会调用递归版本;如果收到两个参数,则称为最终版本。
答案 1 :(得分:1)
在你的情况下,有明显不同的方法,如
quint32 bank_bit_0 = Bit((y / (16 * m_num_pipes)) ^ x_3);
quint32 bank_bit_1 = Bit((y / (8 * m_num_pipes)) ^ x_4);
quint32 bank_bit_2 = Bit((y / (m_num_pipes)) ^ x_5);
quint32 bank = MakeByte(bank_bit_0, bank_bit_1, bank_bit_1);
会调用你的帮助函数
template <typename T1, typename T2>
constexpr T1 MakeByte(T1 byte, quint32 pos, T2 bit)
注意:在C ++ 17中,您甚至可以编写代码:
template <typename Tuple, std::size_t ... Is>
constexpr auto MakeByte_(const Tuple& tuple, std::index_sequence<Is...>)
{
return ((std::get<Is>(tuple) << Is) | ...) ;
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
return MakeByteHelper(std::make_tuple(bit, bits...),
std::make_index_sequence<1 + sizeof...(T2)>());
}
答案 2 :(得分:0)
我在Coding Den Discord服务器上通过eracpp
使用fold expression制作了一个解决方案:
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
T1 byte = bit;
quint32 pos = 0;
((byte |= ((bits & 1) << ++pos)), ...);
return byte;
}
这将代码缩减为一个函数,但确实需要C ++ 17。这将参数从左到右作为LSB到MSB,但这可以用于从左到右作为MSB到LSB(与二进制布局相关):
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits) {
uint32_t pos = sizeof...(bits);
T1 byte = bit;
((byte = (byte << 1) | bits), ...);
return byte;
}
最后,他们建议我没有测试过非C ++ 17解决方案:
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits) {
T1 byte = bit;
using init_t = int[];
(void)init_t{((byte = (byte << 1) | bits), 0)...};
return byte;
}