Translunar
Translunar

Reputation: 3816

How does one increase precision in C++ templates in a template typename-dependent manner?

I have a templated math function which takes two values, does some math to them, and returns a value of the same type.

template <typename T>
T math_function(T a, T b) {
  LongT x = MATH_OP1(a,b);
  return MATH_OP2(x,a);
}

I want to store intermediate values (in x) in a type which is basically the long version of T (above, called LongT). So, if T is float, I want x to be a double; and if T is an int, I want x to be a long int.

Is there some way to accomplish this? I tried enable_if, but it seems that I would really need an enable_if_else.

I'd prefer to have the compiler figure out what to use for LongT on its own. I'd rather not have to specify it when I call the function.

Upvotes: 4

Views: 704

Answers (5)

Leonid Volnitsky
Leonid Volnitsky

Reputation: 9144

template<typename T> struct LongT  {typedef T type;}

template<> struct LongT<int8_t>    {typedef int16_t type;}
template<> struct LongT<int16_t>   {typedef int32_t type;}
template<> struct LongT<int32_t>   {typedef int64_t type;}
template<> struct LongT<int64_t>   {typedef intmax_t type;}
// same for unsigned ...

template<> struct LongT<float>     {typedef double type;}
// template<> struct LongT<double> {typedef long double type;}

Used like so:

typename LongT<T>::type  x;

Upvotes: 1

You can define a type mapping that will yield the needed type:

template <typename T> struct long_type;
template <> struct long_type<int> {
   typedef long type;
};
template <> struct long_type<float> {
   typedef double type;
};

And then use that metafunction:

template <typename T>
T math_function(T a, T b) {
  typename long_type<T>::type x = MATH_OP1(a,b);
  return static_cast<T>(MATH_OP2(x,a));
}

With this particular implementation, your template will fail to compile for any type other than the ones for which you have provided the long_type trait. You might want to provide a generic version that will just map to the itself, so that if the input is long long int that is what is used (assuming no larger type in your architecture).

Upvotes: 5

Mikhail
Mikhail

Reputation: 21789

In C++11 you can use auto keyword to retrieve type of the MATH_OP. It is safe and handled by compiler.

auto x = MATH_OP1(a,b);

Upvotes: 3

ForEveR
ForEveR

Reputation: 55897

You can write your own specialisations of your own structure, or use something like

#include <type_traits>

template<typename T>
void func(const T& f, const T& s)
{
   typename std::conditional
   <
      std::is_same<T, float>::value,
      double,
      typename std::conditional
      <
        std::is_same<T, int>::value,
        long,
        void
      >::type
   >::type X(f + s);
   (void)X;
}

int main()
{
   func(1, 1);
   func(1.0f, 1.0f);
   func(1.0, 1.0);
}

http://liveworkspace.org/code/8f75236b880a3fe210a8751e485a47ed

But write your own specializations as suggest David or Mark is pretty clever.

Upvotes: 0

Mark B
Mark B

Reputation: 96281

Assuming you don't need to handle T=long for example, just create traits for int and float:

template <typename T>
struct LongT;

template <>
struct LongT<int>
{
    typedef long value_type;
};

template <>
struct LongT<float>
{
    typedef double value_type;
};

template <typename T>
T math_function(T a, T b) {
  typename LongT<T>::value_type x = MATH_OP1(a,b);
  return MATH_OP2(x,a);
}

Upvotes: 5

Related Questions