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?

Problem Description:

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.

Solution – 1

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>();
}

Solution – 2

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.

Solution – 3

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>();
    });
}

This is the closest solution to what you posted. The lambda there is a type with a templated operator().


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>();
    });
}

This solution is using generic lambdas, but is still a templated lambda in reality. idx is taking the type of std::integral_constant which can be sent as template parameter since its conversion operator to std::uint32_t is constexpr.

Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject