0

The idea is to call similar Runge-Kutta function templates in a loop instead of doing it one by one. I've looked at similar solutions, I also tried void*, but was not able to apply to my problem due to conversion errors.

EDIT: these function templates are supposed to be used with fixed types, it's an overkill, but I would like to see whether there is an elegant solution.

Here is the full code:

#include <iostream>
#include <iomanip>
#include <cmath>
#include <functional>

template <typename X, typename F, typename H = double>
X runge_kutta1(const X &x, const F &f, H h)
{
    return x + h * f(x);
}

template <typename X, typename F, typename H = double>
X runge_kutta2(const X &x, const F &f, H h)
{
    X k1 = f(x);
    return x + 0.5 * h * (k1 + f(x + h * k1));
}

struct pair
{
    double v;
    double w;

    pair(double v, double w)
        : v{v}, w{w}
    {
    }
};

inline pair operator*(double h, pair p)
{
    return {h * p.v, h * p.w};
}

inline pair operator+(pair p1, pair p2)
{
    return {p1.v + p2.v, p1.w + p2.w};
}

inline std::ostream &operator<<(std::ostream &stream, const pair &p)
{
    stream << p.v << ", " << p.w;
    return stream;
}

int main() {
    {
        double x = 0.0;
        double x1 = 1.0;
        double lambda = 2;
        double h = 1.0E-3;
        pair p{1.0 / lambda, 0.0};

        const std::function<pair(pair)> cat =
            [&lambda](const pair &p) { return pair{p.w, lambda * sqrt(1.0 + p.w * p.w)}; };
        while (x + h < x1)
        {
            p = runge_kutta1(p, cat, h);
            x = x + h;
        }
        p = runge_kutta1(p, cat, x1 - x);
        pair expected = {cosh(lambda * x1) / lambda, sinh(lambda * x1)};
        pair error = p + -1.0 * expected;

        std::cout << std::setprecision(18) << "runge_kutta1:\nFinal result: " << p << "\n";
        std::cout << "Error: " << error << "\n\n";
    }

    {
        double x = 0.0;
        double x1 = 1.0;
        double lambda = 2;
        double h = 1.0E-3;
        pair p{1.0 / lambda, 0.0};

        const std::function<pair(pair)> cat =
            [&lambda](const pair &p) { return pair{p.w, lambda * sqrt(1.0 + p.w * p.w)}; };
        while (x + h < x1)
        {
            p = runge_kutta2(p, cat, h);
            x = x + h;
        }
        p = runge_kutta2(p, cat, x1 - x);
        pair expected = {cosh(lambda * x1) / lambda, sinh(lambda * x1)};
        pair error = p + -1.0 * expected;

        std::cout << "runge_kutta2:\nFinal result: " << p << "\n";
        std::cout << "Error: " << error << "\n";
    }
}

What I would like to have (the actual algorithm is simplified for the sake of readability):

std::vector<?> functions{runge_kutta1, runge_kutta2}; // two just as an example
double p = 1.0;
double h = 1.0E-3;
double lambda = 2;
const std::function<pair(pair)> cat =
        [&lambda](const pair &p) { return pair{p.w, lambda * sqrt(1.0 + p.w * p.w)}; };
for (const auto& func : functions) {
    double t = func(p, cat, h);
    std::cout << t << "\n";
}
  • Templates are not values. So your solution is the wrong path, it leads nowhere. You need to back up and reframe the problem, not try to solve it with a "vector of templates". Vectors store values, templates are not values. – Yakk - Adam Nevraumont Nov 16 '18 at 18:58
  • @Yakk-AdamNevraumont I'd rather say that template is not a type, rather than not a value - in my view, it makes it clearer. `int` is not a value, but vectors of `int`s do exist. – SergeyA Nov 16 '18 at 19:00
  • @Yakk-AdamNevraumont, I've got that, is there a way to somehow instantiate a template and use it inside std::vector? At least in this problem, where all the instances will have the same types. – Mikhail Krassavin Nov 16 '18 at 19:01
  • 1
    And honestly, there is no need to mention Runge-Kutta at all. Your goal is to call a set of template functions in some sort of containerized manner. No need to show us mathematical calculations to simply state this. – PaulMcKenzie Nov 16 '18 at 19:02
  • @MikhailKrassavin certainly, but it will be pointing to a specific version of it. It will only work with specified argument types. – SergeyA Nov 16 '18 at 19:02
  • There are whole piles of things that can be done. For example, if you had a list of scalar types you want to support, you could store the type-erased version of said functions. For efficiency if you tended to call this on a buffer of values, you could type-erase applying it to a buffer. To work out which is required, this requires actually describing your actual problem, not your failed solution which you think is more than sufficient to solve an unspecified problem we don't have. – Yakk - Adam Nevraumont Nov 16 '18 at 19:05
  • This is known as an X/Y problem. You have a problem Y. You think up a solution "if I can do X, I can solve Y". You then run into problems solving X (possibly because it is impossible) and ask about X. In this case, your X is probably ridiculously more powerful than what you need to solve Y. Imagine you want to kill a spider. You hear about atomic fission, and work out the spider will die if you set off a nuke. When trying to assemble the nuke you run into problems running centrifuges, because for power you have a bike. So you ask how to run a uranium centrifuge with a bike. – Yakk - Adam Nevraumont Nov 16 '18 at 19:07
  • This question is "how do I run a uranium centrifuge with a bike". And it is true, if you solved that problem, the spider would indeed be dead. But that problem cannot be solved; so please tell us exactly about the spider you want killed. And don't say "there are many billions of atoms arrnaged in a specific way I need to randomize". Tell us as best you can what the spider is. – Yakk - Adam Nevraumont Nov 16 '18 at 19:09
  • @Yakk-AdamNevraumont, I do understand that this is ridiculous to some extend to use such an approach (of course, a plain function with fixed arguments would work fine), but I am simply interested in learning something new. – Mikhail Krassavin Nov 16 '18 at 19:13

1 Answers1

2

You can not have a pointer to a function template. You can only have a pointer to specific instantiation of the template. In a same manner, you can't pack a template into std::function - only a specific instantiation of it.

And you can only put objects of the same type in the container - so your pointers will have to be of the same type (i.e. the function they point to should accept the same type of arguments and return the same type).

std::function will have the same limitation - all std::functions inside the container must be of the same type, in terms of return value and arguments.

SergeyA
  • 61,605
  • 5
  • 78
  • 137