Home > OS >  How can I pass a function template as (template) argument to a function template?
How can I pass a function template as (template) argument to a function template?

Time:12-06

Currently, I have a code like

#include <cstdint>
#include <iostream>

template<std::uint32_t idx>
void f() {
    std::cout << "f(" << idx << ")\n";
}

template<std::uint32_t n>
void loop1() {
    f<n>();
    if constexpr (n > 0) {
        loop1<n - 1>();
    }
}

template<std::uint32_t idx>
void g() {
    std::cout << "g(" << idx << ")\n";
}

template<std::uint32_t n>
void loop2() {
    g<n>();
    if constexpr (n > 0) {
        loop2<n - 1>();
    }
}

int main() {
    loop1<4>();
    loop2<2>();
}

It's necessary for f, g, loop1 and loop2 to be function templates. Now, I have to add more functions, but loop1 and loop2 will always be the same, a recursive template loop.

How can I pass the function template as (template) argument to loop to achieve something like:

#include <cstdint>
#include <iostream>

template<std::uint32_t idx>
void f() {
    std::cout << "f(" << idx << ")\n";
}

template<std::uint32_t idx>
void g() {
    std::cout << "g(" << idx << ")\n";
}

template<std::uint32_t n, typename h>
void loop() {
    h<n>();
    if constexpr (n > 0) {
        loop<n - 1, h>();
    }
}

int main() {
    loop<4, h>();
    loop<2, h>();
}

I prefer to pass the function as template argument at compile time, but a solution to pass the function template as function argument at runtime would also solve my problem, e.g.

#include <cstdint>
#include <iostream>

template<std::uint32_t idx>
void f() {
    std::cout << "f(" << idx << ")\n";
}

template<std::uint32_t idx>
void g() {
    std::cout << "g(" << idx << ")\n";
}

template<std::uint32_t n, typename T>
void loop(T h) {
    h<n>();
    if constexpr (n > 0) {
        loop<n - 1>(h);
    }
}

int main() {
    loop<4>(h);
    loop<2>(h);
}

or

#include <cstdint>
#include <iostream>

template<std::uint32_t idx>
void f() {
    std::cout << "f(" << idx << ")\n";
}

template<std::uint32_t idx>
void g() {
    std::cout << "g(" << idx << ")\n";
}

template<std::uint32_t n, template<std::uint32_t> typename F>
void loop(F h) {
    h<n>();
    if constexpr (n > 0) {
        loop<n - 1>(h);
    }
}

int main() {
    loop<4>(h);
    loop<2>(h);
}

Is this even possible? I know, that all of my approaches are wrong syntax. It's just to illustrate, what I want to achieve.

CodePudding user response:

I would replace the template functions f and g by structs F and G respectively, each one with a template method invoke:

struct F {
  template<std::uint32_t idx>
  void invoke() {
    std::cout << "f(" << idx << ")\n";
  }
};

struct G {
  template<std::uint32_t idx>
  void invoke() {
    std::cout << "g(" << idx << ")\n";
  }
};

template<std::uint32_t n, typename F>
void loop(F h) {
    h.template invoke<n>();
    if constexpr (n > 0) {
        loop<n - 1>(h);
    }
}

Nothing stops you from adding template functions f and g that call invoke for F and G respectively:

template<std::uint32_t idx>
void f() {
  F().invoke<idx>();
}

template<std::uint32_t idx>
void g() {
  G().invoke<idx>();
}

CodePudding user response:

You cannot do this with free functions but you can do something similar to what you want to have with class template static member functions:

#include <cstdint>
#include <iostream>

template<std::uint32_t idx>
struct F {
  static void function() {
    std::cout << "f(" << idx << ")\n";
  }
};

template<std::uint32_t idx>
struct G {
  static void function() {
    std::cout << "g(" << idx << ")\n";
  }
};

template<std::uint32_t n, template<std::uint32_t> typename T>
void loop() {
    T<n>::function();
    if constexpr (n > 0) {
        loop<n - 1, T>();
    }
}

int main() {
    loop<4, F>();
    loop<2, G>();
}

Here's a demo.

CodePudding user response:

For this I would recommend a either a generic or a template lambda. The goal of our solution would be to send in a predicate that can receive the parameter idx at compile time.

With template lambda (C 20):

#include <cstdint>
#include <iostream>

template<std::uint32_t idx>
void f() {
    std::cout << "f(" << idx << ")\n";
}

template<std::uint32_t idx>
void g() {
    std::cout << "g(" << idx << ")\n";
}

template<std::uint32_t n, typename F>
void loop(F h) {
    h.template operator()<n>();
    if constexpr (n > 0) {
        loop<n - 1>(h);
    }
}

int main() {
    loop<4>([]<std::uint32_t idx>() {
        return f<idx>();
    });
    loop<2>([]<std::uint32_t idx>() {
        return g<idx>();
    });
}

With generic lambda (compatible with C 14)

#include <cstdint>
#include <iostream>

template<std::uint32_t idx>
void f() {
    std::cout << "f(" << idx << ")\n";
}

template<std::uint32_t idx>
void g() {
    std::cout << "g(" << idx << ")\n";
}

template<std::uint32_t n, typename F>
void loop(F h) {
    h(std::integral_constant<std::uint32_t, n>{});
    if constexpr (n > 0) {
        loop<n - 1>(h);
    }
}

int main() {
    loop<4>([](auto idx) {
        return f<idx>();
    });
    loop<2>([](auto idx) {
        return g<idx>();
    });
}
  • Related