Jump to content
  • Advertisement
Sign in to follow this  
Impz0r

template question

This topic is 4299 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Heya, i've got a question about templates. Lately i played around with templates and tried something like:
namespace math {
 template<typename T> inline T sqrt (T value) {
  return (::sqrt (value));
 }
}

int main (...) {
 float foo = math::sqrt (4.0f); // does work
 long bar = math::sqrt (4); // does not work, the compiler reports: Error 2 error C2668: 'sqrt' : ambiguous call to overloaded function
}

Which makes sense, because the orginal sqrt() function does only take, "long double", "double", "float". So far so good. Now the real question. *g* Is there are a way to detect, if the type T needs to be typcasted and if so, build the function with the required typecast ? Or does this only works with explicit specialization ? Please bear with me and the example, i'm not so into templates ;) Thanks in advance! Impz0r

Share this post


Link to post
Share on other sites
Advertisement
I believe that the most effective way to do this would be via template specializations. Which is also the exact mechanism for accomplishing what you want to do.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
For something simple like you example I agree with daerid. for something more complex you could use std::type_info and typeid(T)

Share this post


Link to post
Share on other sites
the only way to do what you want is manually, because their could never be a case of having the compiler choose automatically which conversion to use in the situation in which the problem is that it doesn't know which version to use.

Looking at what you are doing, there are 2 distinct pieces:

1. You are wrapping the stand-alone overloaded sqrt() functions inside namespace math. You choose to do this using a templated function which should work perfectly well.

2. You want to provide additional overloads which allow the user to pass types other than the 2 supported by the source functions.

The problem with 2 is ... WHICH overloads, if you do provide such a function you are actually hiding the conversion from the client which uses it, and thereby likely making them unaware that there was even a choice.

It would be difficult to determine which implementation is better for:

int val;
...
int result = math::sqrt(val);

should you use float for efficiency, double for accuracy, or long double for reasons largely tied to a particular platform.

What you should do depends on your purpose ... if writing a general math wrapper library you should likely not add overloads ... if you are writing a library for your specific need, then you should simply pick one version (float, double, etc) and use it for integral type up-conversions.

Share this post


Link to post
Share on other sites
Thanks daerid!


So that means, i define for every type another template function like:


namespace math {
template<typename T> inline T sqrt (T value) {
return (::sqrt (value));
}

template<> inline long sqrt (long value) {
return (static_cast<long>(::sqrt (static_cast<float> (value))));
}

... etc ...
}





Hmm doesn't that kinda defeat the purpose of a template ?


@Anonymous Poster

How exactely would look something like this ?


@Xai

First off, thanks for your replay!
So in your second point you mean, let the user typecast like:

namespace math {
template<typename T> inline T sqrt (T value) {
return (::sqrt (value));
}

float foo = math::sqrt (4.0f);
long bar = static_cast<long> (math::sqrt (static_cast<float> (4))); // something like this ?
}



Well that would work i guess and is most likely the "cleanest" solution?


Impz0r

Share this post


Link to post
Share on other sites
You can do it with boost::enable_if and Boost.TypeTraits:

#include <cmath>

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

namespace math {
template< class T >
typename boost::disable_if< boost::is_floating_point< T >, T >::type
sqrt( T value ) {
return static_cast< T >( std::sqrt( static_cast< double >( value ) ) );
}

template< class T >
typename boost::enable_if< boost::is_floating_point< T >, T >::type
sqrt( T value ) {
return std::sqrt( value );
}
}

int main() {
float foo = math::sqrt( 4.0f );
long bar = math::sqrt( 4 );
}






There might be a more elegant way by importing std::sqrt into the math namespace. If that doesn't work, you can also use enable_if as a final default parameter (I like this version better, though).


jfl.

Share this post


Link to post
Share on other sites
Quote:
Original post by Impz0r
Thanks daerid!


So that means, i define for every type another template function like:

*** Source Snippet Removed ***

Hmm doesn't that kinda defeat the purpose of a template ?


Yep. One way to modify only part of the code for each type, but still have a generic function is to use traits.


template<typename T> class CastType;

template<> class CastType<int> {
public:
typedef float Type;
};

... etc ...

template<typename T> inline T sqrt (T value) {
return (static_cast<CastType<T>::Type>(::sqrt (static_cast<float> (value))));
}




(Might need a typename in there when referencing "Type", I always forget those.)

Basicly this lets you replace part of your generic function with specific code for each type (in this case the cast), while keeping the rest of the code generic.

Share this post


Link to post
Share on other sites
Thanks jflanglois!

Unfortunately i'm not quite sure if i follow your solution. I haven't used those boost templates till now. Does boost check the typeid behind the scene to check what kind of a type it was given ? Those checks aren't executed at runtime are they ?

And what did you mean with:
Quote:

"There might be a more elegant way by importing std::sqrt into the math namespace"
?


Impz0r

Share this post


Link to post
Share on other sites
Quote:
Original post by Impz0r
Thanks jflanglois!

Unfortunately i'm not quite sure if i follow your solution. I haven't used those boost templates till now. Does boost check the typeid behind the scene to check what kind of a type it was given ? Those checks aren't executed at runtime are they ?

No, it's all at compile time. The typetraits look like what Solias does with his CastType class: for instance, is_integral< int > is a spcialization of is_integral< T > where is_integral::value is true.

Quote:
And what did you mean with:
Quote:

"There might be a more elegant way by importing std::sqrt into the math namespace"
?

I meant putting a using std::sqrt; as a default instead of implementing math::sqrt in terms of std::sqrt.

[edit: Um, stupid me. I meant this:
namespace math {
template< class T >
typename boost::disable_if< boost::is_floating_point< T >, T >::type
sqrt( T value ) {
return static_cast< T >( std::sqrt( static_cast< double >( value ) ) );
}

using std::sqrt;
}

int main() {
float foo = math::sqrt( 4.0f );
long bar = math::sqrt( 4 );
}
]

Share this post


Link to post
Share on other sites
Thanks Solias & jflanglois!


So to get it right, the solutions of you both are quite similar? Except i don't have to do much, if i'm using boost ? :p

One question about your solution jflanglois. If i read it correctly, all types which are not floats going through the first function and the rest through the second one ? Does this mean, i don't have to implement a bunch of explicit template functions for all kind of types then ? Only the two and i'm done ?

[Edit]
About the "using std::sqrt", i'm not quite sure what it does there. Could you please explain?



Impz0r

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!