Convert a C++ integral type template parameter to a "longer" integral type

1

I have a templated function that accepts an integral type and performs a mathematical operation on two input parameters:

template<typename T, typename R>
R multiply( const T& lhs, const T& rhs ) {
  R ans = static_cast<R>( lhs ) * static_cast<R>( rhs );
  return ans;
}

(For simplicity, assume that types T and R are always unsigned integral types, or that I perform a suitable check to make sure.)

In this contrived example, you can see I am doing a simple multiplication (just for illustration). The idea is to pass a type R that is twice-as-many-bits-wide as type T; this will allow the full product to fit in the return value:

uint64_t Product64 = multiply<uint32_t,uint64_t>( 0xFFFFFFFF, 0xFFFFFFFF );
uint16_t Product16 = multiply<uint8_t,uint16_t>( 0xFF, 0xFF );

I am wondering if there is any way to dispense with the second template parameter, and automatically compute a type that is [at least] twice-as-many-bits-wide as type T at compile time.

Note: Just using the largest available integral type (uint64_t) for R at all times is not a solution for me. Ideally, I want a compile-time-computed type that is just large enough to hold twice-as-many-bits as type T (e.g. when T = uint8_t, R = uint16_t).

It seems to me this should be possible with limits or type_traits or something similar, but so far I haven't thought of a solution. C++11 or later version is fine. I would prefer to avoid boost, but if that is the only way, I would be interested to see a boost-based solution too.

c++
templates
types
asked on Stack Overflow Jan 29, 2019 by Kevin H. Patterson • edited Jan 29, 2019 by Kevin H. Patterson

1 Answer

5

You can make your own template sized types and use them to define your return type, like follows:

#include <cstdint>
template<int N>
struct sized_uint {};
template<> struct sized_uint<8 > { using type=std::uint8_t; };
template<> struct sized_uint<16> { using type=std::uint16_t; };
template<> struct sized_uint<32> { using type=std::uint32_t; };
template<> struct sized_uint<64> { using type=std::uint64_t; };

#include <climits>
template<typename T>
auto multiply(T lhs, T rhs)
{
  using R=typename sized_uint<CHAR_BIT*sizeof(T)*2>::type;
  return static_cast<R>(static_cast<R>(lhs) * static_cast<R>(rhs));
}

See it live.

answered on Stack Overflow Jan 29, 2019 by Ruslan

User contributions licensed under CC BY-SA 3.0