Conditional template type math

Viewed 131

I have a C++ function that takes in an array of uint8_t performs various functions on it that require a uint16_t and emits a smaller uint8_t array.

For a uint16_t input I might need a uint32_t array, etc.

Is there a way to establish this relationship using templates? Something like (in pseudocode):

template <typename T, typename U = sizeof(T) * 2>

Unfortunately, I am actually working in a mix of and so there's no avoiding a lot of copy-pasting, but I was wondering if this was an option in some cases.

3 Answers

In , you could provide a helper template function, which you can decltype to find the return type of the array you want to return.

#include <cstdint>
#include <type_traits> // std::is_same_v

template <typename T> 
auto ret_type_helper()
{
   if constexpr (std::is_same_v <T, std::uint8_t>)         return std::uint16_t{};
   else if constexpr (std::is_same_v <T, std::uint16_t>)   return std::uint32_t{};
   else if constexpr (std::is_same_v <T, std::uint32_t>)   return std::uint64_t{};
   // else if constexpr... for another type.....
}

template <typename T>
auto func(const std::array<T /*, size */>& arr)
{
   // use for getting the array's type, which you want to return from the function
   using ReType = decltype(ret_type_helper<T>());

   std::array<ReType /*, size */> result;

   // ... code
   return result;
}

You can't do it automatically, but with some manual work it is possible:

template<typename> struct double_size_type;

template<> struct double_size_type<std::uint8_t> {
    using type = std::uint16_t;
};

template<> struct double_size_type<std::uint16_t> {
    using type = std::uint32_t;
};

template<> struct double_size_type<std::uint32_t> {
    using type = std::uint64_t;
};

template<typename T, typename U = typename double_size_type<T>::type> {
    // ...
};

For each supported type T you need to provide a specialization of double_size_type<T>.

We can make the size of the int a template argument, so Int<32> means 32 bit integer. Then you can do Int<sizeof(other_int) * 8 * 2>, which means int with twice the size of other_int:

#include <cstdint>
#include <type_traits>

// signed
template <std::size_t size>
using Int = std::conditional_t<size==8, std::int8_t,
                std::conditional_t<size==16, std::int16_t,
                    std::conditional_t<size==32, std::int32_t,
                        std::conditional_t<size==64, std::int64_t, void>>>>;

// unsigned
template <std::size_t size>
using UInt = std::conditional_t<size==8, std::uint8_t,
                std::conditional_t<size==16, std::uint16_t,
                    std::conditional_t<size==32, std::uint32_t,
                        std::conditional_t<size==64, std::uint64_t, void>>>>;

int main() { 
    // twice size of a regular int
    Int<sizeof(int) * 8 * 2> i2 = 0; 
}
Related