views:

310

answers:

3

I'm having trouble with some valarray function pointer code:

double (*fp)(double) = sin;
valarray<double> (*fp)(const valarray<double> &) = sin;

The first compiles, the second gives:

error: no matches converting function 'sin' to type 'class std::valarray<double> (*)(const class std::valarray<double>&)'
+4  A: 

You speak about std::sin in the title, but then assign ::sin.

valarray<double> (*fp)(const valarray<double> &) = std::sin;

That should work. Note, that you should qualify all uses of sin, though most implementations will inject the name to the global namespace even if you include <cmath> (that is non-standard behavior).

Edit: unfortunately, you're out of luck. The standard says about sin(valarray<T> const &) the following (26.3.3.3).

This function shall return a value which is of type T or which can be unambiguously converted to type T.

Optimizations performed by gcc are granted by the standard. The code above is not guaranteed to work.

avakar
It doesn't work for me on GCC either. Same error message :(
Johannes Schaub - litb
You're right, on GCC it indeed fails. I'd say it's a bug though. The standard clearly defines `template<class T> valarray<T> sin (const valarray<T>
avakar
I agree. Looks like a GCC madness :(
Johannes Schaub - litb
I take it back, see my edit (and AProgrammer's answer).
avakar
+6  A: 

26 3.1/3

Any function returning a valarray is permitted to return an object of another type, provided all the const member functions of valarray are also applicable to this type.

The aim is to allow template expressions to be used to optimize the result (i.e. looping one time on the whole array doing each times the computation, directly assigning to the resulting valarray<> instead of building a temporary).

z = sin(x+y);

can be optimized to

for (i = 0; i < N; ++i)
   z[i] = sin(x[i] + y[i]);
AProgrammer
+1, that makes sense. xD
Johannes Schaub - litb
+7  A: 

This compiles, using the __typeof__ GCC extension. Looks like GCC's valarray uses expression templates to delay calculation of the sinus. But that will make the return type of the sin template not exactly valarray<T>, but rather some weird complex type.

#include <valarray>

template<typename T> struct id { typedef T type; };
int main() {
  using std::valarray;
  using std::sin;

  id<__typeof__(sin(valarray<double>()))>::type (*fp)(const valarray<double> &) = sin;
}

Edit: See AProgrammer's standard quote for why GCC is fine doing that.

Edit: Standard compliant workaround

Doing this without __typeof__ in a strictly Standard conforming way is a bit tricky. You will need to get the return type of sin. You can use the conditional operator for this, as Eric Niebler has shown. It works by having the sin function not actually called, but only type-checked. By trying to convert the other branch (the one which is actually evaluated) of the conditional operator to that same type, we can generate a dummy parameter just to be able to deduce the type of the function pointer:

#include <valarray>

using std::valarray;

template<typename T> struct id {
  typedef T type;
};

struct ded_ty {
  template<typename T>
  operator id<T>() { return id<T>(); }
};

template<typename E, typename T>
id<T(*)(valarray<E> const&)> genFTy(T t) { 
  return id<T(*)(valarray<E> const&)>(); 
}

template<typename T>
void work(T fp, id<T>) {
  // T is the function pointer type, fp points
  // to the math function.
}

int main() {
  work(std::sin, 1 ? ded_ty() : genFTy<double>(std::sin(valarray<double>())));
}

If you want to get the address right away, you can write work so it returns fp again.

template<typename T>
T addy(T fp, id<T>) { return fp; }

Now, you can finally write a macro to encapsulate the conditional operator trickery, and use it when you want to get the address of any such math function.

#define DEDUCE(FN,Y) (1 ? ded_ty() : genFTy<Y>(FN(std::valarray<Y>())))

To get the address and pass it to some generic function, the following works then

std::transform(v1.begin(), v1.end(), v1.begin(),
  addy(std::sin, DEDUCE(std::sin, double)));
std::transform(v2.begin(), v2.end(), v2.begin(),
  addy(std::cos, DEDUCE(std::cos, double)));
Johannes Schaub - litb
I am simultaneously impressed with your MacGyver-like ability to bend a conditional operator into the solution to this problem, and horrified that it was necessary to do so. :)
j_random_hacker
wow. i do not understand. i'm going to have to study this...
Robert Karl
@robert, feel free to ask questions if there are some parts you don't understand. I will try to give an explanation for them then. Also be sure to read eric nieblers explanation of his use of `operator?` here: http://www.artima.com/cppsource/foreach2.html
Johannes Schaub - litb