Sign in to follow this  
Impz0r

template question

Recommended Posts

Impz0r    166
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
daerid    354
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   
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
Xai    1838
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
Impz0r    166
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
jflanglois    1020
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
Solias    564
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
Impz0r    166
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
jflanglois    1020
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
Impz0r    166
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
Guest Anonymous Poster   
Guest Anonymous Poster
Impz0r what is the functionality you want. Is it to return the type entered unless its an int and then returnn a long int?


#include <cmath>

template<typename T> struct Sqrt_return{typedef T R_type;};
template<> struct Sqrt_return<int> {typedef long int R_type;};

namespace math
{
template<typename T>
typename Sqrt_return<T>::R_type sqrt( T value )
{
return static_cast< Sqrt_return<T>::R_type >( std::sqrt( static_cast<double>( value ) ) );
}

}

Share this post


Link to post
Share on other sites
Impz0r    166
Well, i guess what i'm trying to do is, have one interface and don't bother the user with it specifics, like typecasting.

@Anonymous Poster

Your solution looks similar to Solias's. I guess it's a commmon approach to do something i want to.


Well i will give both, yours/Solias and jflanglois a shot.

Thanks alot for your help guys!



Impz0r

Share this post


Link to post
Share on other sites
jflanglois    1020
Quote:
Original post by Impz0rSo 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

Well, no. Solias was showing you a way to select which type to convert to based on the template type, while mine selects a function depending on the template type. They use similar concepts, though.

Quote:
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 ?

That is correct. You could use Solias' CastType to further select what type to convert to before calling std::sqrt.

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

That just imports the function name sqrt from namespace std to namespace math. What this means is that std::sqrt() and math::sqrt() are the same function.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Yes Impz0r it is a common thing aswell as many more like turning an int into its own type,(which is very handy). I would suggest you have a look at a very good book.
Modern C++ Design, Generic Programming and Design Patterns

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Oops messed the link up lol
Modern C++ Design, Generic Programming and Design Patterns

Share this post


Link to post
Share on other sites
Impz0r    166
About this std::sqrt import thing, does this mean, i whether can use std::sqrt or math::sqrt within my code? Does it also affects the rest of the std:: lib ? I mean because i imported the std::sqrt, the std is using my math::sqrt function from now on?

@Anonymous Poster

Your link is messed up. But thanks anyway :)


Impz0r

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
twice??? I dont think so!!!
http://safari.awprofessional.com/0201704315

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Quote:
Original post by Impz0r
About this std::sqrt import thing, does this mean, i whether can use std::sqrt or math::sqrt within my code? Does it also affects the rest of the std:: lib ? I mean because i imported the std::sqrt, the std is using my math::sqrt function from now on?


Impz0r

The std::sqrt func is being used by your namespace maths. If you don't include the "std::" then scope rules say that your function will call itself recursively.

Share this post


Link to post
Share on other sites
jflanglois    1020
Quote:
Original post by Impz0r
About this std::sqrt import thing, does this mean, i whether can use std::sqrt or math::sqrt within my code? Does it also affects the rest of the std:: lib ? I mean because i imported the std::sqrt, the std is using my math::sqrt function from now on?

It does nothing to the std namespace. It just says: I'm going to use std::sqrt() as if it was a function in math. As an example:
namespace one {
void function() {}
}

namespace two {
using one::function;
}

int main() {
two::function(); // This is actually a call to one::function()
}

Share this post


Link to post
Share on other sites
Xai    1838
Quote:
@Xai

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

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


Impz0r


Yeah, that's pretty much what I meant. And I am not saying it is the "best" solution for all purposes. I was only trying to remind everyone in this discussion that you cannot use any of these proposed systems to autocast ints / shorts / chars, etc ... without invisibly picking which of the 3 existing versions to use ... which may or may not be what the user would prefer.

For a game which uses one type of float throughout, an autocasting solution is good, for a general purpose library I don't think it is. Although you could do something like have a single typedef "StandardFloatType" or something ... which would allow the client to easily pick which float gets used for their project.

Share this post


Link to post
Share on other sites
Impz0r    166
Thanks Xai!

Well it's for a game i'm building right now, within all my projects i have the demand to build it, like it is for a greater audience not only for me ;)

Ah and right now, i'm using the solution jflanglois presented and it works like a charm! I really recommend this method. And besides boost rocks!


Impz0r

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this