3
votes

As an exercise for my personal enlightenment, I implement vector math with expression templates. I want to implement some operations that apply the same unary function to all elements to a vector expression. So far, I do this.

My base vector expression template is implemented like this

template <typename E>
class VectorExpr {
public:
  int size() const { return static_cast<E const&>(*this).size(); }

  float operator[](int i) const { return static_cast<E const&>(*this)[i]; }

  operator E& () { return static_cast<E&>(*this); }

  operator E const& () const { return static_cast<const E&>(*this); }
}; // class VectorExpr

Then, an object supposed to be a vector will look like this

class Vector2 : public VectorExpr<Vector2> {
public:
    inline size_t size() const { return 2; }

    template <typename E>
    inline Vector2(VectorExpr<E> const& inExpr) {
    E const& u = inExpr;
    for(int i = 0; i < size(); ++i)
        mTuple[i] = u[i];
   }

private:
   float mTuple[2];
};

Let's say I want to apply std::sin to all elements of an expression

template <typename E>
class VectorSin : public VectorExpr<VectorSin<E> > {
    E const& mV;

public:
    VectorSin(VectorExpr<E> const& inV) : mV(inV) {}

    int size() const { return mV.size(); }

    float operator [] (int i) const { return std::sin(mV[i]); }
};

Question => If I want to add more functions, I copy-paste what I do for the sin function, for every single function (like cos, sqrt, fabs, and so on). How I can avoid this kind of copy-pasting ? I tried things and figured out I'm still low in template-fu. No boost allowed ^^

2
Since you're learning, you should try C++11's lambdas together with std::for_each() (examples)CAFxX
Thanks for the suggestion, I will ! Actually, I should be able to do without C++11 as well. I run numeric code often on clusters, where the compilers are not exactly bleeding edge.Monkey

2 Answers

5
votes
template <typename F, typename E>
class VectorFunc : public VectorExpr<VectorFunc<F, E> > {
    E const& mV;

public:
    VectorSin(VectorExpr<E> const& inV) : mV(inV) {}

    int size() const { return mV.size(); }

    float operator [] (int i) const { return f(mV[i]); }

    // this assumes the Functor f is default constructible, this is
    // already not true for &std::sin. Adding the constructor that
    // takes f, is left as an exercise ;)
    F f;
};
2
votes

In addition to the answer by pmr, The standard <cmath> functions aren't functors, so you couldn't use them directly to specify unique specialisations of your class - i.e. you wouldn't have a separate template instantiation for std::sin versus std::cos (which is what I gather you're aiming for? correct me if I've misunderstood you on that).

You could create a wrapper in order to map a function pointer to a distinct type, e.g.

#include <iostream>

template< void (*FuncPtr)() > struct Func2Type
{
    void operator() () { FuncPtr(); }
};

void Hello() { std::cout << "Hello" << std::endl; }
void World() { std::cout << "world" << std::endl; }

int main()
{
    Func2Type<Hello> test1;
    Func2Type<World> test2;
    test1(); 
    test2();
}

That way you could use them as template arguments in the same way as a normal functor class