4

Consider this hypothetical code snippet :

 template<?? function>
 void loop() {        
    for(int i =0; i < 10000; ++i ) {
        function(i)
    }
 }

 //...

 void print(int i) {
    std::cout << i << '\n';
 }

 //...

 loop<print>();

is it possible to do something like that in C++ ? So far I know that function pointers and generic functors can be passed through templates parameters (like in std::sort), but is there a way to make it so that no actual object is passed during runtime and the call to "print" is completely direct (ie no indirection) ? ie transferring the actual function by "value" in the template, like it's possible to pass integer in a template using template <int i> or some other integral types.

jwodder
  • 54,758
  • 12
  • 108
  • 124
lezebulon
  • 7,607
  • 11
  • 42
  • 73
  • 1
    I'd rather go for `typename Function` and a `Function function` parameter so it's just `loop(print)`. – chris Jun 07 '14 at 00:57
  • I'm precisely trying to avoid the potential overhead of having an extra layer of indirection, because this is just passing a function pointer. This is not really a question about performance, just wondering if it's possible. – lezebulon Jun 07 '14 at 00:59
  • I am surprised you didn't see this question: http://stackoverflow.com/questions/1174169/function-passed-as-template-argument Nearly identical title, and the answer already included in the question. – jogojapan Jun 07 '14 at 01:31
  • @lezebulon: If your compiler is not atrociously bad (like, using the standard library templates on it is near impossible performance-wise), don't worry about that indirection: It will get compiled out if your adaptor is stateless. – Deduplicator Jun 07 '14 at 01:31

5 Answers5

3

Of course, it is possible. Template non-type parameters can be of function pointer type. In the most simplistic case, specifically tailored to your simple example it might look as follows

template <void (*function)(int)>
void loop() {        
   for(int i = 0; i < 10000; ++i ) {
       function(i);
   }
}

Note that C++03 stated that a valid template argument for such template must be a pointer to a function with external linkage. C++11 removed the external linkage requirement.

yizzlez
  • 8,757
  • 4
  • 29
  • 44
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

As has been noted in other answers, there is such a thing as function template parameters. However, sometimes it is desirable for the template parameter to be a type. This is especially useful with other kinds of template metaprogramming, e.g. to make a list of functions.

The following code allows you to wrap functions in types. There are different variants of the WRAP_FUNC macro for wrapping a function. The suffix-less one is for use outside of templates, the _T variant is for use within templates, and the _TI variant is for inheriting within templates (see below).

Note that in order to work with references, std::forward is used. Also, it must be used as written, with another parameter pack, CallArgs, for the types of arguments which call() is called with, as opposed to Args, which are the argument types for the given function.

#include <utility>
#include <stdio.h>

// This goes into a header.

template <typename R, typename... Args>
struct Helper {
    template <R (*Func) (Args...)>
    struct Wrapper {
        template <typename... CallArgs>
        static R call (CallArgs && ... args)
        {
            return Func(std::forward<CallArgs>(args)...);
        }
    };
};

template <typename R, typename... Args>
struct Helper<R, Args...> MakeHelper (R (*func) (Args...));

#define WRAP_FUNC(func) decltype(MakeHelper(func))::Wrapper<func>
#define WRAP_FUNC_T(func) typename decltype(MakeHelper(func))::template Wrapper<func>
#define WRAP_FUNC_TI(func) decltype(MakeHelper(func))::template Wrapper<func>

// Let's try it out.

static double test_func (int x, double y)
{
    return x + y;
}
using TestFunc = WRAP_FUNC(test_func);

template <typename Func>
static void CallFunc ()
{
    double r = Func::call(4, 6.0);
    printf("%f\n", r);
}

int main ()
{
    CallFunc<TestFunc>();
}

If the function can only be defined after being passed to the class that needs it (because it itself calls to the class that calls it and we don't want to separate its definition and declaration), inheritance can be used to work around the circular dependency. Here, the _TI form of the macro needs to be used if this is within a template.

template <.....>
class Qux {
    struct CallbackFunc;
    using MyFoo = Foo<CallbackFunc>;

    static void callback_func ()
    {
        // Foo calls us but we call Foo!
        MyFoo::bar();
    }
    struct CallbackFunc : public WRAP_FUNC_TI(callback_func) {};
};
Ambroz Bizjak
  • 7,809
  • 1
  • 38
  • 49
  • 1
    `Args&&... args` and `Func(std::forward(args)...)`. – chris Jun 07 '14 at 01:14
  • 1
    @chris IIRC that doesn't work right because the Args is not a template parameter pack of the call() function but is predefined by Helper. You should try it out if you think it's right. Put simply, using it like you suggested is not following the recipe at http://en.cppreference.com/w/cpp/utility/forward . – Ambroz Bizjak Jun 07 '14 at 01:19
  • Oops, didn't pick up on that. Sorry. – chris Jun 07 '14 at 02:34
2

A simpler function template, without any bells and whistles :)

#include <iostream>

template<typename Function>
void loop(Function function) {        
    for(int i =0; i < 10000; ++i ) {
        function(i);
    }
}

void print(int i) {
    std::cout << i << '\n';
}

int main()
{
   loop(print);
   return 0;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • There's an indirection through the function-pointer going on, though that might be inlined anyway. – Deduplicator Jun 07 '14 at 01:35
  • 1
    And strictly speaking this isn't a solution to the question which asks for passing a function through a template parameter, not a combination of a template parameter and a function argument. – Ambroz Bizjak Jun 07 '14 at 01:36
1

Since you can pass pointers to templates as non-type parameters, you can pass function pointers to them as well. You may also consider one to pass function objects.

 #include <iostream>

 template <void (*f)()>
 void loop() {
   f();
 }

 template <typename F>
 void loop() {
   F()();
 }

 void F() {
   std::cout << "F" << std::endl;
 }

 struct G {

   void operator()() const {
     std::cout << "G" << std::endl;
   }

 };

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

Prints

 F
 G
mpark
  • 7,574
  • 2
  • 16
  • 18
  • Maybe also add that the adaption from type to function is generally less flexible than the reverse, because one can use objects with state and they might be replaceable... – Deduplicator Jun 07 '14 at 01:28
0

There is no way to use a function rather than a function pointer as a template argument. There fairly few entities which can be used a template arguments. The non-type template parameters are listed in 14.1 [temp.param] paragraph 4:

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,
  • std::nullptr_t.
Community
  • 1
  • 1
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380