0

I wish to write a C++ template function which in turn uses some "C" functions and exploits function overloading.

For example, I need to write a function myAbs using templates which make appropriate calls to fabs or abs defined in math.h, based on the input parameter type. How to do this?

#include <math.h>
template<typename T>
T abs(T x)
{
   // I need to write an efficient code here!
   // If it is 'double' and 'float' I may be able to compare  the      
   // sizeof(Type) and call 'return fabs(x)' or 'return abs(x)'.
   // But this is not a good solution as two types can be of same size! 

}

Note: I simply used it as an example to explain my question. I already know that such a function "abs" is already available in <cmath>.

anatolyg
  • 26,506
  • 9
  • 60
  • 134
user4661268
  • 49
  • 1
  • 7

4 Answers4

5

Templates might not be the answer here. Consider just overloading:

inline float myAbs(float x) { return fabsf(x); }
inline double myAbs(double x) { return fabs(x); }
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • 2
    Just beware of ambiguous overloads. See http://stackoverflow.com/questions/1374037/ambiguous-overload-call-to-absdouble – Andrew Henle Oct 17 '16 at 15:24
4

C++17 solution (mostly just for fun), which guarantees that the branch is taken at compile time:

template <typename T>
T my_abs(T x)
{
    if constexpr(std::is_same<T, double>{})
    {
        return std::fabs(x);
    }
    else if constexpr(std::is_same<T, float>{})
    {
        return std::abs(x);
    }
    else
    {
        // Produces a compiler error:
        struct invalid_type;
        return invalid_type{};
    }
}

on wandbox

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
0

You could use C++11 type traits:

#include <math.h>
#include <type_traits>
template<typename T>
T abs(T x)
{
    if(std::is_same<T,double>::value)
    {
        //do double stuff
    }
    else if(std::is_same<T,float>::value)
    {
        //do float stuff
    }
    else
    {
        //deal with unsupported type
    }
}

Hope that helps.

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • 1
    Not really. If there is a `abs` function which takes 2 `std::string` for example, and you make a branch for it in your `abs` function, every call to your `abs` will fail. – Rakete1111 Oct 17 '16 at 15:15
  • @Rakete1111 Are you suggesting that the deduction of type will fail? Then just put the type explicitly. – The Quantum Physicist Oct 17 '16 at 15:17
  • "Compile time checks" can sound misleading to beginners (i.e. it may sound like the branches are taken at compile time). Consider changing that to "You could use C++11 type traits". – Vittorio Romeo Oct 17 '16 at 15:18
  • @TheQuantumPhysicist I'm suggesting that the template instatiation will fail for any `T`, because every branch has to be considered, and thus compiled. But if the branches are designed to be taken for different types, the code for one type (float) will not compile the branch for std::string, even if it is never taken. – Rakete1111 Oct 17 '16 at 15:20
  • @Rakete1111 I modified the text in my answer to fit with your suggestion. But the case you provided is very weird and unrealistic for this problem. Anyway, I solve this issue by force-casting the types at return. (even if I have to do a `reinterpret_cast`). I fall into this problem often with LAPACK, but solving it is easy ;-) – The Quantum Physicist Oct 17 '16 at 15:21
  • 1
    This solution will perform run-time branching, which is sub-optimal, assuming it will even compile (as per Rakete's comments). Probably we should use type traits and call out to a helper function. – AndyG Oct 17 '16 at 15:27
  • @AndyG I disagree; it won't do run-time branching. If you pass `double` to this function, for example, at compile time the compiler will decide that other branches are effectively not accessed and will be optimized out. It's like doing `if(true) //dosomething else //do another`. Do you really think the compiler will even consider the other case? It will be run-time only when you disable optimizations or if you involve volatile variables, which you wouldn't do for math problems. – The Quantum Physicist Oct 17 '16 at 15:30
  • @TheQuantumPhysicist: The point about an optimizing compiler is a fair one. I would still prefer type traits for delegating to a helper function in general as it's less likely to trigger a compiler error because all branches must be evaluated as per Rakete's comment. – AndyG Oct 17 '16 at 15:39
  • @AndyG It's a preference thing :-) . I prefer the method I provided in the interest of readability (again, it's a taste thing). For that problem that Rakete talked about, the solution is simply to force-cast stuff to (T), and problem solved! – The Quantum Physicist Oct 17 '16 at 15:42
  • @TheQuantumPhysicist Force casting is not a really nice solution though. Also, the compiler can't optimize out code that doesn't even compile. – Rakete1111 Oct 18 '16 at 14:14
  • @Rakete1111 I'm sorry, you're mixing two issues here. If you force-cast, then the code will compile. Whether it's nice or not, that's opinion driven. I'm totally against writing many overloads. That's my opinion. Remember that you can always clean your code by assigning to a new variables rather than recasting the same variable again and again. And *then*, after you're done casting, the compiler will succeed and will optimize out all invalid `if` options, and you'll get optimal performance. – The Quantum Physicist Oct 18 '16 at 14:35
  • @TheQuantumPhysicist Yes, force casting is in my opinion not nice for that problem :) But ok – Rakete1111 Oct 18 '16 at 14:38
0

You can explicitly specialize the function templates:

template<>
double abs<double>(double x) {
    return fabs(x);
}

If you want to overengineer more, you can employ tag-dispatch to cut down on the repetition and make up for it in tag selection.

krzaq
  • 16,240
  • 4
  • 46
  • 61