Sign in to follow this  
johnstanp

How to design a vector class to use any sqrt function?

Recommended Posts

Well, the title says it all. I was just wondering how it could be possible to "assign" a sqrt function to a Vector class, in order to parametrize the norm( or its inverse ) computing inside the class? My vector class is a template one, and one possible sqrt algorithm is a static function inside a math class( also a template one ). So I have:
template<typename T>
class Vector3
{
       public:
              ...
              T length()const;

       private:
               T x_[3];
};

template<typename T>
class Math
{
       public:
              ...
              static Real sqrt( const Real& )const
};


I want to be able to make the length function use the standard std::sqrt() or my sqrt function or any other one. Sorry if my english is not good enough... [Edited by - johnstanp on July 7, 2008 5:26:57 PM]

Share this post


Link to post
Share on other sites
Here's an interesting idea.

It uses compile-time evaluation to determine the value.

While it says for integers, I don't see any real reason why it wouldn't work for any data type.


As for sqrt() function, you can't, that one can only be evaluated during run-time.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Here's an interesting idea.

It uses compile-time evaluation to determine the value.

While it says for integers, I don't see any real reason why it wouldn't work for any data type.


As for sqrt() function, you can't, that one can only be evaluated during run-time.


Interesting, but that function can only be used for integers?

Maybe my use of the term "compile time" is not appropriate: I just want to implement a general enough method that would give me the possibility to use inside my class any sqrt algorithm. I was just thinking of functors but I wanted to know if there were better or more elegant ways to achieve that.

PS:I'll change the title of the topic...to make it less specific.

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
I want to be able to make the length function use the standard std::sqrt() or my sqrt function or any other one.

#include <cmath>
#include <iostream>

template <typename T, T (*SQRT)(T)>
struct Vec3
{
T x, y, z;

T length() const {
return SQRT(x*x + y*y + z*z);
}
};

typedef Vec3<float, std::sqrt> Vec3Std;

int main()
{
Vec3Std v = {1, 2, 3};

std::cout << v.length() << std::endl;
}

Share this post


Link to post
Share on other sites
If i understand you correctly you want something like this:



template<typename T, typename Math>
class Vector3
{
public:
...
T length()const
{
// blabla
Math::sqrt(5.0);
}


private:
T x_[3];
};

class StdMath
{
public:
static float sqrt(float x)
{
return std::sqrt(x);
}
};

// Vector using std
typedef Vector<float, StdMath> StdVector;

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
Quote:
Original post by johnstanp
I want to be able to make the length function use the standard std::sqrt() or my sqrt function or any other one.

*** Source Snippet Removed ***


Thank you for being so quick to reply...I'll implement it.
Will it work for my function template<typename T> T Math<T>::sqrt( const T& )?

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
Quote:
Original post by Antheus
Here's an interesting idea.

It uses compile-time evaluation to determine the value.

While it says for integers, I don't see any real reason why it wouldn't work for any data type.


As for sqrt() function, you can't, that one can only be evaluated during run-time.


Interesting, but that function can only be used for integers?


Yeah, unless you extend it to rationals. Alternatively you could look at D, which has support for floats as template parameters. Most of the time, there's not much benefit to this.

Quote:
Maybe my use of the term "compile time" is not appropriate: I just want to implement a general enough method that would give me the possibility to use inside my class any sqrt algorithm. I was just thinking of functors but I wanted to know if there were better or more elegant ways to achieve that.

PS:I'll change the title of the topic...to make it less specific.



You could use a traits mechanism:


namespace calc_traits
{

template<typename T>
struct sqrt
{
static T calc(T x) { return std::sqrt(x); }
};

} // close namespace


Call calc_traits::sqrt<T>::calc(number) to get the square root of number, which should be a T. Use template specialisation to add support for your custom types (or other peoples custom types):


namespace calc_traits
{

template<>
struct sqrt<big_num>
{
static big_num calc(const big_num &x) { /* custom implementation */ }
};

} // close namespace

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd

You could use a traits mechanism:


namespace calc_traits
{

template<typename T>
struct sqrt
{
static T calc(T x) { return std::sqrt(x); }
};

} // close namespace


Call calc_traits::sqrt<T>::calc(number) to get the square root of number, which should be a T. Use template specialisation to add support for your custom types (or other peoples custom types):


namespace calc_traits
{

template<>
struct sqrt<big_num>
{
static big_num calc(const big_num &x) { /* custom implementation */ }
};

} // close namespace


I had problems understanding the traits mechanism which I thought was related to type information...I understand the method, and I think that I could combine it with what I have in mind. Actually, I understand there's a special syntax for specifying pointers to member functions and there's a possibility to instanciate a class using such a pointer. I'll search the correct syntax.

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
I had problems understanding the traits mechanism which I thought was related to type information...

It usually is used for that, but that's not the heart of the concept. Traits allow you to use 3rd party code with yours without changing the 3rd party implementation. Concept maps in the next revision of the standard takes this idea even further.

The other plus of this is that you don't need to add an extra warty template parameter to your vector class.

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd
Quote:
Original post by johnstanp
I had problems understanding the traits mechanism which I thought was related to type information...

It usually is used for that, but that's not the heart of the concept. Traits allow you to use 3rd party code with yours without changing the 3rd party implementation. Concept maps in the next revision of the standard takes this idea even further.

The other plus of this is that you don't need to add an extra warty template parameter to your vector class.


I didn't pay close attention to the mechanism you provided. Actually, depending of the type parameter of my vector class, a template specialization would be chosen: so I won't need passing a pointer. The problem is that I want too, to be able to use std::sqrt() which gives better results thant my sqrt algorithm. As the std::sqrt() uses the same types as my function, if I am not mistaken, I won't be able to use that mechanism.

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
Quote:
Original post by the_edd
Quote:
Original post by johnstanp
I had problems understanding the traits mechanism which I thought was related to type information...

It usually is used for that, but that's not the heart of the concept. Traits allow you to use 3rd party code with yours without changing the 3rd party implementation. Concept maps in the next revision of the standard takes this idea even further.

The other plus of this is that you don't need to add an extra warty template parameter to your vector class.


I didn't pay close attention to the mechanism you provided. Actually, depending of the type parameter of my vector class, a template specialization would be chosen: so I won't need passing a pointer. The problem is that I want too, to be able to use std::sqrt() which gives better results thant my sqrt algorithm. As the std::sqrt() uses the same types as my function, if I am not mistaken, I won't be able to use that mechanism.


So you need template specialisation?

template <class T>
class Vector3
{
public:
T x, y, z;

// generic empty method
T length (void) { return T(); }
};

// integer square root function
int iSqrt (int x)
{
/* calculate integer sqrt */
return x;
}

// implementation for Vector3 <int>
template <> int Vector3 <int>::length (void)
{
return iSqrt (x * x + y * y + z * z);
}

// implementation for Vector3 <float>
template <> float Vector3 <float>::length (void)
{
return std::sqrt (x * x + y * y + z * z);
}

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
Quote:
Original post by lexs
If i understand you correctly you want something like this:


*** Source Snippet Removed ***


That's it!


A template function seems more flexible :


template<typename T>
class Vector3
{
public:
...
template<typename Math>
T length() const
{
return Math::sqrt(...);
}


private:
T x_[3];
};

class StdMath
{
public:
static float sqrt(float x)
{
return std::sqrt(x);
}
};


Vector<float> Vector;
float v = Vector::length<StdMath>();


Share this post


Link to post
Share on other sites
Quote:
Original post by Moomin

A template function seems more flexible :

*** Source Snippet Removed ***


Thank you very much Moomin!!!
That's exactly what I wanted.

Thank you guys for your precious help!!!

PS:well, thre3dee, I can specialize member functions of a template class?
It seems that GCC doesn't allow it, or have I done something incorrectly?

[Edited by - johnstanp on July 7, 2008 9:31:01 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
Quote:
Original post by Moomin

A template function seems more flexible :

*** Source Snippet Removed ***


Thank you very much Moomin!!!
That's exactly what I wanted.

Thank you guys for your precious help!!!

PS:well, thre3dee, I can specialize member functions of a template class?
It seems that GCC doesn't allow it, or have I done something incorrectly?


I haven't used GCC but I assumed template specialisation was part of standard C++; amirong?

Share this post


Link to post
Share on other sites
Quote:
Original post by thre3dee
I haven't used GCC but I assumed template specialisation was part of standard C++; amirong?


Actually, I cannot specialize a member function of a class template without specializing the whole class. I've got various compile errors, that informs me that all member variables have incomplete types.

[Edited by - johnstanp on July 9, 2008 11:49:10 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
Quote:
Original post by thre3dee
I haven't used GCC but I assumed template specialisation was part of standard C++; amirong?


Actually, I cannot specialize a member function of a class template without specializing the whole class. I've got various compile errors, that informs me that all member variables have incomplete types.


That's right. You can't specialize a member function of a class template because you can make a specialization of this class template that doesn't contain that function.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
As for sqrt() function, you can't, that one can only be evaluated during run-time.
You find the below template meta-programming of mine interesting:
It provides compile-time C_FLOORSQRT(n) and C_CEILSQRT(n).
#define C_FLOORSQRT(n) (floorSqrtn<n>::ret)
template <int n, int a = (n>>1), int b = -1>
struct floorSqrtn {
enum { ret = a*a <= n && n < (a+1)*(a+1) ? a : floorSqrtn<n, ((n/((n/a + a)>>1) + ((n/a + a)>>1))>>1), a>::ret };
};
template <int n, int a>
struct floorSqrtn<n, a, a> {
enum { ret = a };
};
template <>
struct floorSqrtn<1, 0, -1> {
enum { ret = 1 };
};

#define C_CEILSQRT(n) (ceilSqrtn<n>::ret)
template <int n, int a = (n>>1), int b = -1>
struct ceilSqrtn {
enum { ret = (a-1)*(a-1) < n && n <= a*a ? a : ceilSqrtn<n, ((n/((n/a + a)>>1) + ((n/a + a)>>1))>>1), a>::ret };
};
template <int n, int a>
struct ceilSqrtn<n, a, a> {
enum { ret = a };
};
template <>
struct ceilSqrtn<1, 0, -1> {
enum { ret = 1 };
};

Share this post


Link to post
Share on other sites
Quote:
Original post by Moomin
Quote:
Original post by johnstanp
Quote:
Original post by lexs
If i understand you correctly you want something like this:


*** Source Snippet Removed ***


That's it!


A template function seems more flexible :

*** Source Snippet Removed ***


I think that would lead to inconsistent usage. I know I'd get annoyed at having to supply a template parameter every time I wanted to do something simple like get a vectors length.

The overhead with traits is a one-time upfront cost for each type that needs to be supported (and the default implementation will suffice for the majority of uses).

I can't deny that they're complicated, but it's the best tool for the job, here, IMHO.

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
Quote:
Original post by the_edd
Quote:
Original post by johnstanp
I had problems understanding the traits mechanism which I thought was related to type information...

It usually is used for that, but that's not the heart of the concept. Traits allow you to use 3rd party code with yours without changing the 3rd party implementation. Concept maps in the next revision of the standard takes this idea even further.

The other plus of this is that you don't need to add an extra warty template parameter to your vector class.


I didn't pay close attention to the mechanism you provided. Actually, depending of the type parameter of my vector class, a template specialization would be chosen: so I won't need passing a pointer.


I don't know what you mean by "passing a pointer". There are no pointers involved in the code I posted. All functions are class-scope static.

Quote:
The problem is that I want too, to be able to use std::sqrt() which gives better results thant my sqrt algorithm. As the std::sqrt() uses the same types as my function, if I am not mistaken, I won't be able to use that mechanism.


By default std::sqrt is used (as it's called by the primary template). It does exactly what you want.

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd
I don't know what you mean by "passing a pointer". There are no pointers involved in the code I posted. All functions are class-scope static.


I was thinking of the other possibility of adding a template parameter, a pointer to a function. I was not referring to your code, but talking of the benefit of using it.

Quote:

By default std::sqrt is used (as it's called by the primary template). It does exactly what you want.


I understand that, but what if the functions I want to use, use the same type as the primary template?

[Edited by - johnstanp on July 9, 2008 4:44:50 PM]

Share this post


Link to post
Share on other sites
The template suggestion I made above was before the OP edited the post. Templated calculation of values doesn't apply in this case.

The simplest solution still remains using templates and default arguments as was mentioned before:
template < class T >
struct math_traits {
Error - unspecialized // faulty struct definition to fail
};

template <>
struct math_traits<float> {
static float sqrt(float value);
};

template <>
struct math_traits<int> {
static int sqrt(int value);
};

template < class T, class Math = math_traits<T> >
struct Vector3D {

T length() {
return Math::sqrt(....);
}
};



This has the added benefit of end-user class remaining the same syntax-wise, so there's no need to change the existing code.

Share this post


Link to post
Share on other sites
Well, the_edd, I was wondering if bignum could be a typedef, an alias of a primary type, hence removing the need of using a custom type, to select, for the same primary type, a different implementation( algorithm ).

Share this post


Link to post
Share on other sites
Quote:
Original post by johnstanp
Well, the_edd, I was wondering if bignum could be a typedef, an alias of a primary type, hence removing the need of using a custom type, to select, for the same primary type, a different implementation( algorithm ).


Ah I see. No, I intended you to think of it as a user defined class.

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd
Quote:
Original post by johnstanp
Well, the_edd, I was wondering if bignum could be a typedef, an alias of a primary type, hence removing the need of using a custom type, to select, for the same primary type, a different implementation( algorithm ).


Ah I see. No, I intended you to think of it as a user defined class.


Okay.
I could wrapp the primary types in my own classes, but I would have to overload the usual operators for those primary types.
Well, I took the most painful path, that is to say, adding a template parameter to the definition of my Vector3 class and to all of the classes that have a vector3 member object...


template<typename Real,class MathLib>
class Vector3
{
public:
...

private:
...
};

...

template<typename Real,class MathLib>
Real Vector3<Real,MathLib>::length()const
{
return MathLib::SQRT( x[0]*x[0] + x[1]*x[1] + x[2]*x[2] );
}





Of course, MathLib must define static functions( for example SQRT ): I prefer doing it that way, to avoid storing an instance of MathLib, for every Vector3 instanciated...And ah, I had to overload "operator=" and "Vector3( const Vector3& )" to allow "type conversion" between two Vector3 using different "Math Libraries".

[edit] That's the solution given by lexs. So thank you lexs. And thank you the_edd for your disponibility.[/edit]

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