views:

79

answers:

3

I have defined a class in C++ which holds an array of scalars of type T for which I want to define operators like sin, cos, etc. For defining the meaning of sin applied on an object of this class I need to know the meaning of sin applied on the single scalar type T. This means I need to use appropriate math libraries (corresponding to the scalar type T) within the class. Here's the code as it is now:

template<class T>
class MyType<T>
{
    private:
        std::vector<T> list;

    // ...

        template<class U> friend const UTP<U> sin(const UTP<U>& a);
        template<class U> friend const UTP<U> cos(const UTP<U>& a);
        template<class U> friend const UTP<U> tan(const UTP<U>& a);

    //...
};

template<class T> const UTP<T> sin(const UTP<T>& a)
{
   // use the sin(..) appropriate for type T here 
   // if T were double I want to use double std::sin(double)
   // if T were BigNum I want to use BigNum somelib::bigtype::sin(BigNum)
}

Currently, I have code that exposes the appropriate math library (using namespace std;) and then use ::sin(a) inside the sin function for my class MyType. While this works, it seems like a major hack.

I see that C++ traits can be used to store instance specific information (like which set of math functions to use when T is double, when T is BigNum, etc..)

I want to do something like this: (I know this doesn't compile but I hope this conveys what I want to do)

template<T>
struct MyType_traits {
};

template<>
struct MyType_traits<double> {
    namespace math = std;
};

template<>
struct MyType_traits<BigNum> {
    namespace math = somelib::bigtype;
};

and then in redefine my MyType class as:

template<T, traits = MyType_traits<T> >
class MyType
{
// ...
}

and then use traits::math::sin in my friend function. Is there a way in which I can obtain the correct namespace (parameterized by T) containing the math functions?

A: 

It's not the specific answer you're looking for, but wouldn't using template specialization be a simpler option?

As in...

template <typename T> T sin(T& t)
{
    // does nothing
}

template <> float sin(float& t)
{
    ...
}

template <> double sin(double& t)
{
    ...
}

And so on?

ShaderOp
I don't want to (re!)define what it means to take sine of doubles and floats! I want to use *appropriate* (existing) library functions within my class depending on the template parameter (double or BigNum or ..)
chillu
+5  A: 

Isn't argument-dependent look-up good enough?

#include <cmath>
#include <iostream>

namespace xxx {
class X
{
};

X sin(X) { return X(); }
} //xxx

std::ostream& operator<< (std::ostream& os, xxx::X)
{
    return os << "X";
}

template <class T>
void use_sin(T t)
{
    using std::sin; //primitive types are not in a namespace,
                    //and with some implementation sin(double) etc might not be available
                    //in global namespace
    std::cout << sin(t) << '\n';
}

int main()
{
    use_sin(1.0);
    use_sin(xxx::X());
}

This would work for X, because sin(X) is defined in the same namespace as X. If you expect that not to be so, this probably won't help...

UncleBens
So you're suggesting I bunch up the math functions for each set (double, BigNum, ..) and put each in one namespace. Then, in the place where I'll want to have argument dependent lookup, I'll expose them all with using namespace xxx; declarations? This is pretty much how it is right now. Can't I "assign" namespaces like I have shown in the question? What's the closest equivalent to that in C++? With the correct namespace "stored" in some "variable" math (see question) I can always use math::sin and be sure that the correct function is being called.
chillu
@cillu: The point is: if the argument type is in a namespace, then the same namespace will be searched for a suitable function to call. When the compiler sees `xxx::X x; sin(x);`, it will look into the global namespace *and namespace xxx* to find a suitable function to call. - I think you can't assign namespaces like that, although this doesn't mean there couldn't be other solutions. - Namespaces are pain. IMO people should just place their overloads of standard functions in the std namespace (as far as the rules - which could be relaxed - allow) :)
UncleBens
@UncleBens: I'm completely fine by letting the compiler doing the lookup. There's only one problem I see: Whenever I use a new type `MyType<T>` I'll have to come back to this file and expose the math functions for the type `T` in `namespace xxx`. I'd really like to not touch the template headers everytime for changes that are being effected by the client!
chillu
@cillu: The above should work in all cases, if T's corresponding `sin` function is either in the global namespace, in the std namespace or in the same namespace as T itself. There shouldn't be any need to modify anything. In the example, the correct overload for T=X is picked automatically without any change to `use_sin`.
UncleBens
@chillu: No need to do that. If the `class` or `struct` is in namespace `ns`, the compiler will automatically search namespace `ns` when resolving `sin(x)`. The client just has to ensure his libraries are `#include`-d before he first uses your function. The only case when you need `using namespace` in your library is when the operations are in a different namespace eg. `float` vs. `std::sin`.
jpalecek
@jpalecek: Awesome. Thanks for clearing that up. I suppose UncleBens said the same thing in the previous comment. So I guess the answer is to just let the compiler do it's thing (argument dependent lookup) and chuck traits for now.
chillu
A: 

I'm including this answer because I finally managed to get what I want (with help from very nice folk on ##c++ at irc.freenode.net). This method enables both ADL as well as a static set of places (xxx::math) to look in for the definition of the math functions.

This way, if the type parameter T of the class Test is such that:

  1. if T defines math functions as members then we can use ADL and not touch (add to) the namespace xxx::math.
  2. if T doesn't define math functions as members but uses functions from a specific namespace then we can add to the namespace xxx::math like in the example below.

Library looks like this:

#include <vector>
#include <cmath>

namespace xxx {

// include the usual math
namespace math {
    using std::asin;
}

template <class T>
class Test
{
    std::vector<T> array;

    public:
    Test(const typename std::vector<T>::size_type length)
    {
        assert(length >= 1);
        array.assign(length, T(0.0));
    }
    friend std::ostream& operator<<(std::ostream& out, const Test<T>& a)
    {
        out << "(";
        std::copy(a.array.begin(), a.array.end(), std::ostream_iterator<T>(out, ", "));
        out << "\b\b)";
        return out;
    }

    template<class U> friend const Test<U> asin(const Test<U>& a);
};

template<class U> const Test<U> asin(const Test<U>& a)
{
    using math::asin;

    Test<U> ret(a.array.size());
    for (typename std::vector<U>::size_type i = 0; i < a.array.size(); ++i) 
        ret.array[i] = asin(a.array[i]);

    // note how we use have a using math::asin; and then a call to asin(..) here,
    // instead of a math::asin(..). This allows for ADL.

    return ret;
}

} // xxx

Client looks like this:

#include <iostream>
#include <boost/math/complex.hpp>

// client, with some foresight, includes complex math
namespace xxx { namespace math {
    using boost::math::asin;
} }

#include "test.h"

// demo
int main(int argc, char **argv)
{
    using std::cout; using std::endl;

    xxx::Test<double> atest(3);
    cout << "atest: " <<  atest << endl;
    cout << "asin(atest): " <<  asin(atest) << endl;
    cout << endl;

    xxx::Test<std::complex<double> > btest(3);
    cout << "btest: " <<  btest << endl;
    cout << "asin(btest): " <<  asin(btest) << endl;
    cout << endl;

    return 0;
}
chillu