I'm writing code for scientific calculation which uses trigonometric functions, and since I need to use not only float/double but also multiprecision floating point numbers, I'm templatizing the functions and classes.

Let's say I'm simply writing a function to calculate `sin(pi*x) * cos(pi*x)`:

```
template <class Real>
Real sincos(const Real& x);

```

How can I use proper versions of trigonometric functions and pi value? Multiprecision floating point libraries usually have their own version of trigonometric functions, and `std::` versions are defined only for `float`, `double`, and `long double`, and `M_PI` is not even standard.

I tried putting function pointers as arguments, but `std::` versions are overloaded functions not template, so I should put it like `(double (*)(double)&std::sin)` which hurts readability and it's hard to use.

```
template <class Real>
Real sincos(const Real& x,
            Real (*sin)(const Real&), Real (*cos)(const Real&), const Real& pi)
{
    return sin(pi*x) * cos(pi*x);
}

// I don't think it's well designed function if it's hard to use like this.
double s = sincos<double>(0, (double (*)(double))&std::sin,
                             (double (*)(double))&std::cos,
                             M_PI);

my_mpf = sincos<my_mpf>(0, somewhere::my_sin, somewhere::my_cos, my_mpf_version_of_pi);

```

The problem is that there are lots of math functions needed, so I can not simply put those into the function parameters.

How should I generalize those calculations?