mercredi 25 octobre 2017

What is the best way to reduce ambiguity for templated variadic functions?

I'm currently trying to make a variadic templated function named MakeByte that will take any number of arguments of bits, and put them into one byte. Here's an example of the usage for it:

// 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);

I have three functions, in a separate header involved:

  • template <typename T1, typename... T2> T1 MakeByte(T1 bit, T2... bits), the function that will be used by external code that calls the recursive functions.
  • template <typename T1, typename... T2> T1 MakeByte(T1 byte, quint32 pos, T1 bit, T2... bits), the recursive function that iterates over each bit. This function has additional arguments to keep track of the final byte, and the current position to put the next bit in.
  • template <typename T1, typename T2> T1 MakeByte(T1 byte, quint32 pos, T2 bit), the function that deals with the final bit.

Here's the complete 3 definitions:

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...);
}

The issue is that, when compiling with g++, I get this error:

/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...);
          ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

At this point, I renamed the two recursive functions, in case there was ambiguity:

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...);
}

This code does compile, but I can't help but feel this is a bit of a hack. From a design perspective, what is the best way to reduce ambiguity within variadic templated functions?

Aucun commentaire:

Enregistrer un commentaire