3

I'm trying to code a numeric method that can accept a function as an argument that itself has arbitrary arguments. The best way to do this seems to be with a variadic template. This https://stackoverflow.com/a/15960517/3787488 answer is nearly exactly what I need, but my code wont compile.

This is my test case;

#include<iostream>
#include<vector>
#include<fstream>
#include<functional>
#include<iomanip>


double testfunction(double x, double k);

template<typename... Ts>
using custom_function_t = double(*) (double, Ts...);

template< typename... Ts>
double test( custom_function_t<Ts...> f, Ts... args, double min, double max, int m, int n)
{
    double ans=0;
    double step=(max-min)/100.00;
    for (double x=min;x<=max;x=x+(max-min)/100)
    {
        ans=ans+(step/6.0)*(f(x, args...)+4*f(x+0.5*step, args...)+f(x+step, args...));
    }
    return(ans);
}

int main()
{
    double ans=0;
    std::cout<<test(testfunction,2.0,0.0,1.0,0,0)<<endl;
    return(0);
}

double testfunction(double x, double k)
{
    double ans=0;
    ans=x*x*k;
    return(ans);
}

Where the function 'test' should take the function 'testfunction' and numerically integrate it (integration of 2*x^2 from 0 to 1=2/3).

Compiling with gcc 4.7.3 c++11 I get the errors;

note: template<class ... TS> double test (custom_function_t<Ts ...>, Ts ..., double, double, int, int)
note: template argument deduction/substitution failed:
note: candidate expects 5 arguments, 6 provided
Community
  • 1
  • 1
Mike Pegg
  • 43
  • 6
  • What is the `f` in this line supposed to mean (and what happens if you remove that `f`)? `using custom_function_t = double(*f) (double, Ts..);` – JSF Dec 01 '15 at 16:21
  • Also, what version of gcc are you using and what compile-time options to select the version of C++ to use? If you google your error message, you find mostly cases of someone trying a C++11 feature without having (or enabling) C++11 support. – JSF Dec 01 '15 at 16:34
  • I'm compiling using GCC in code::blocks with 'gave g++ follow the coming C++0x ISO C++ language standard [-std=c++0x]' option. – Mike Pegg Dec 01 '15 at 16:35
  • The `f` in the line `using custom_function_t = ...` is definitely a problem. If you remove it, the compiler should go further. I am still seeing a problem with auto deduction of the template parameters (http://ideone.com/H2eKG5). The problem goes away when I am explicit with the template parameters (http://ideone.com/B8vGpX). It appears to be a g++ bug but we'll have to see if other compilers have the same problem. – R Sahu Dec 01 '15 at 16:44
  • I was suggesting `using custom_function_t = double(*) (double, Ts...);` Compared to what you have, that removes the excess `f` and adds a missing `.` Then I think you need to enable C++11, not c++0x. Then you need `std::` on your use of `cout`. After all that, you have too few `double`s passed in the test (`testfunction` consumes 2 and `test` consumes 2 more but you only passed 3 total). – JSF Dec 01 '15 at 16:45
  • From what I can tell doesnt an extra double get passed from test to testfunction, making the 4? – Mike Pegg Dec 01 '15 at 16:54
  • What if Ts... args in this method call has no parameter** . Do you see anything wrong there then?? – Naman Dec 01 '15 at 17:02
  • Could this be of help?? `main.cpp:14:8: note: candidate template ignored: substitution failure [with Ts = double] double test( custom_function_t f, Ts... args, double min, double max, int m, int n)` – Naman Dec 01 '15 at 17:15
  • Seems like there is some mismatch in calling `testfunction`. This piece of code is really confusing though. :( – Naman Dec 01 '15 at 17:19
  • @MikePegg I see I was incorrect about the number of `double`s required. You should edit your code and your post to correct all the minor problems mentioned above, and if necessary switch to C++11. Then there seems to be a template deduction question left. – JSF Dec 01 '15 at 19:22
  • @nullpointer I'm open to other ideas, but to do what I need to this is the most commonly offered way, – Mike Pegg Dec 01 '15 at 20:45
  • @JSF Done, I'll give it a go with C++11 again tomorrow morning. – Mike Pegg Dec 01 '15 at 21:12
  • `2.0,0.0,1.0,0,0` might be a bit easier to read if you used your spacebar. In fact that's true of most of the code. – Jonathan Wakely Dec 02 '15 at 13:58
  • @JSF, `-std=c++0x` means **exactly** the same thing as `-std=c++11` so that won't help at all. – Jonathan Wakely Dec 02 '15 at 14:06

2 Answers2

1

In C++ (since 2011), something like this is best done using a lambda, caught via a template parameter, which can be any callable object:

#include<iostream>
#include<iomanip>
#include<cassert>

template<typename Func>   // anything that allows operator()(double)
double test(Func const&func, double x, const double max,
            const unsigned num_intervals=100)
{
  assert(num_intervals>0);
  const double dx=(max-x)/num_intervals;
  const double dx_half=0.5*dx;
  const double dx_third=dx/3.0;
  const double dx_two_third=dx_third+dx_third;
  double sum = 0.5*dx_third*func(x);
  for(unsigned i=1; i!=num_intervals; ++i) {
    sum += dx_two_third * func(x+=dx_half);
    sum += dx_third     * func(x+=dx_half);
  }
  sum+=dx_two_third* func(x+=dx_half);
  sum+=0.5*dx_third* func(x+=dx_half);
  return sum;
}

double testfunction(double, double);

int main()
{
  std::cout<<std::setprecision(16)
           <<test([](double x) { return testfunction(x,2.0); }, 0.0,1.0)
           <<std::endl;
}

double testfunction(double x, double k)
{
  return x*x*k;
}

Note also that I avoided to evaluate the functions more than once for the same value.

Walter
  • 44,150
  • 20
  • 113
  • 196
0

The compiler can't deduce the size of the parameter pack from the supplied arguments unless the pack is at the end.

As you've discovered, it works if you re-order the arguments.

Another option is to save it from having to deduce them by giving the arguments explicitly:

test<double>(testfunction, 2.0, 0.0, 1.0, 0, 0)

I'm not sure why GCC can't deduce the arguments from the function pointer you pass, but the EDG compiler can't either, giving this error:

"var.cc", line 20: error: no instance of function template "test" matches the
          argument list
            argument types are: (double (*)(double, double), double, double,
                      double, int, int)
      test(testfunction, 2.0, 0.0, 1.0, 0, 0);
      ^

My build of Clang 3.8.0 crashes on the original code, and 3.5.0 rejects it. If I get rid of the alias template and declare test as

template< typename... Ts>
double test( double(*f)(double, Ts...), Ts... args, double min, double max, int m, int n)

Then Clang 3.50 and 3.80 both compile it happily.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521