Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Passing function as template argument [C++]

Started by deffer Jan 14, 2007 at 5:17 AM 17 replies 17.4k views
Original Post
deffer
deffer
Sort of theoretical problem from the land of C++. I would like to create a templated struct/class that has a function as a template parameter. It would be a simple one-argument function. The type of the struct is meaningless, since it would be used in expressions, so template arguments can be deduced as much as possible. The goal is: as little verbosity as possible. First sample:
template< typename R, typename Arg, R Fun(Arg) >
struct S
{ /*...*/ };

/* usage: */
int my_fun(float&);
S<int, float&, my_fun>()
Too verbose - I have to write types by hand. The name of the function should be enough (forget about the overloads for the moment). Second sample - let's use some deducing:
template< typename R, typename Arg >
struct fun_type_deducer
{
   template< R Fun(Arg) >
   S<R, Arg, Fun>
   instantiate(void) const {
      return S<R, Arg, Fun>();
   };
};

template< typename R, typename Arg >
inline fun_type_deducer<R, Arg>
get_fun_type_deducer( R Fun(Arg) ) {
   return fun_type_deducer<R,Arg>();
};

#define SS(Fun_)    ( (get_fun_type_deducer(Fun_)).instantiate<(Fun_)>() )

/* usage: */
int my_fun(float&);
SS(my_fun)
But I have to use a macro, which is polutting the space all around.
I'm out of ideas for now. Is it possible to do this with templates only?
DMINATOR
DMINATOR
I know I needed something similar for the SDL threads, here is a link, maybe it would help:

clicky
nmi
nmi
Maybe this article is what you need:
http://www.codeproject.com/cpp/TTLFunction.asp

It allows you to write code like this:
function< void () > //no parameters, no return valuesfunction< void (int) > //1 int parameter, no return valuesfunction< void (int, char) > //2 parameters, no return valuesfunction< int (int, char) > //2 parameters and return values...

by using partial specialisation of templates.

Another interesting article is this one:
http://www.codeproject.com/cpp/ImpossiblyFastCppDelegate.asp

Maybe you just want to combine those two concepts.
Julian90
Julian90
DMINATOR and nmi i think you are both misinterprating what he wants (i did the same thing at first) he doesnt need to pass the type of a function as a template paramater or a mem func ptr to a function expecting a non mem func ptr but rather the function itself as a template paramater e.g.
int DoStuff(int i){    // ...}std::string DoMoreStuff(float i){    // ...}// and using the first code snippet in his postint main(){    S<int, int, DoStuff>() // create a S with DoStuff    S<std::string, float, DoMoreStuff>() // create a S with DoMoreStuff    // except for he doesnt want to have to pass the return type and    // argument types as template paramaters}// hence the second approach allows#define MakeS(func) get_fun_type_deducer(func).instanciate<func>()int main(){    MakeS(DoStuff); // create a S with DoStuff    MakeS(DoMoreStuff); // create a S with DoMoreStuff    // and his question was weather theres a way to do this without the macro    // i.e. with templates only}


P.S. im still experimenting trying to find a way but the unrelated responses where slightly annoying

Edit: on rereading that i sound a bit arrogant so ill just explain quickly why partial template specialization wont work, in order to use partial template specialization you of course have to have the thing you specializing as a template paramater, however inorder to do this the you would have to include the type of the Func to be passed as a template paramater also which means to instantiate S you still need to include the type of the function as the compiler can only determine it when calling templated functions and as such you would be forced to use the same technique as in the second code snippet by the OP resulting in no gains.
deffer
deffer
@DMINATOR:
The linked discussion is about passing the function by value, not by template parameter, so it doesn't relate, unfortunately.

@nmi:
TTLFunction keeps the function by value, so it doesn't relate...
ImpossiblyFastCppDelegate is as verbose as my first sample.

@Julian90:
Yes, that's my problem.

@AP:
Loki keeps the function by value...
Glak
Glak
in C++ when you create an instance of a templated type you need to specify the type. When you call a templated function you do not need to specify the type. In the standard library there is a type called pair. To create a pair object to send to a function you can call the constuctor:

pair(5,"hi")

however that is too verbose, particularly if you are using more complicated types. Thus the standard also includes a templated function called make_pair:

make_pair(5,"hi")

so that is the solution that you need. Maybe call it something like wrap_function or whatever.
Julian90
Julian90
ok im going to bed, didnt come up with anything useful tonight but i was wondering this wouldnt be an acceptable solution?

// wrap someFunc up as a typestruct someFunc{    static int Exec(float&)    {        // ...    }};template<typename Func>struct S{    // ...};int main(){    S<someFunc>(); // create S with someFunc}


Edit:

Quote:
In C++ when you create an instance of a templated type you need to specify the type. When you call a templated function you do not need to specify the type. In the standard library there is a type called pair. To create a pair object to send to a function you can call the constuctor:

pair(5,"hi")

however that is too verbose, particularly if you are using more complicated types. Thus the standard also includes a templated function called make_pair:

make_pair(5,"hi")

so that is the solution that you need. Maybe call it something like wrap_function or whatever.


Good idea and thats how his second code snippet works, the problem is that the third template paramater, the actual function, isnt dependant on any of the things that can be used to construct S and hence you need a second stage in which you specify it a second time but as a template paramater instead of a function paramater resulting in the need for the macro.
deffer
deffer
Quote:
Original post by Julian90
ok im going to bed, didnt come up with anything useful tonight but i was wondering this wouldnt be an acceptable solution?

*** Source Snippet Removed ***


Syntax S() is acceptable, but defining an additional helper struct is overly verbose, even more than my original first sample...
Glak
Glak
Perhaps if you actually explained your problem more people could help. I believe that I suggested the right answer before but you did not reply. I decided to implement my solution and it was actually pretty tricky to get the return type without specifying it in the template parameters.

Is this what you are looking to be able to do?

double Example(int i)
{
return i;
}

int main()
{
double d=7;
d=wrap(Example)(5);
cout<}
deffer
deffer
Quote:
Original post by Glak
Perhaps if you actually explained your problem more people could help. I believe that I suggested the right answer before but you did not reply.


I'm sorry, I though Julian90 already provided the answer to your post.

Let me alaborate a little: What you were proposing is a deduction of template parameters. While we do can deduce types quite easily with this method, it is still unclear how we should go about deducing values (function is a value, not a type).

Quote:
Original post by Glak
I decided to implement my solution and it was actually pretty tricky to get the return type without specifying it in the template parameters.

Is this what you are looking to be able to do?

double Example(int i)
{
return i;
}

int main()
{
double d=7;
d=wrap(Example)(5);
cout<}


Yes. But beware that (internally) Example is not to be passed/kept by value (by member of some struct, for example), but "remembered" as a template parameter.



My goal is to rewrite some of the standard (or not[smile]) functionality operating on function pointers (like for_each, transform, filter_iterator, for example). While those keep/pass around functions by values, I noticed that the compiler is able to perform next to no optimisations, even in most trivial cases, not to mention some more complicated ones. Same code using function pointers as template parameters turns out to be inlined/optimised perfectly fine.

I got some code working fine, but I got some issues with wrapping it in a nice way. That's the story.
Bregma
Bregma
Quote:
Original post by deffer
My goal is to rewrite some of the standard (or not[smile]) functionality operating on function pointers (like for_each, transform, filter_iterator, for example). While those keep/pass around functions by values, I noticed that the compiler is able to perform next to no optimisations, even in most trivial cases, not to mention some more complicated ones. Same code using function pointers as template parameters turns out to be inlined/optimised perfectly fine.


I'm really lost trying to understand what you're trying to do.

What do you mean when you say "keep/pass around functions by values?" I am unfamiliar with the concept in C++ outside of keeping/passing pointers to functions or using function objects.

I have also never had a problem wit hthe compiler optimizing std algorithms, whether with pointers to functions or function objects (or various standard binders operating on either of the above), assuming I've enabled optimization. The generated assembly shows the compiler inlines appropriately given the visibility and size of function bodies.

Do you have some sample code that doesn't optimize properly?

--smw
Stephen M. Webb
Professional Free Software Developer
Vorpy
Vorpy
The Boost Function Object Adapter Library provides unary_traits and binary_traits classes that I think do what you want. unary_traits::argument_type and unary_traits::result_type are the types of the argument and result of function or function object T.
Julian90
Julian90
Quote:
What do you mean when you say "keep/pass around functions by values?" I am unfamiliar with the concept in C++ outside of keeping/passing pointers to functions or using function objects.


Yes he wants to pass a function pointer but instead of passing it as an argument of a function he wants to pass it as a template paramaters since it is possible (with exceptions, e.g. floating point values) to pass any compile time constant as a template paramater, for example...
// this is what your thinking of (i think)void for_each(const int* start, const int* end, int (*function_ptr)(int)){    for (; start != end; start++)    {        function_ptr(*start);    }}// and heres what he wants// notice the function pointer which was previously an argument to for_each// is know a template paramatertemplate<int (*function_ptr)(int)>void for_each(int* start, int* end){    for (; start != end; start++)    {        function_ptr(*start);    }}


his problem comes when you try to hava arbitary return types and argument types for the function pointer in the first on you still only have to specify the name of the function and the compiler will deduce the types however in the second one you end up having to specify the type of the function as well as the name despite the fact that the compiler already knows the type.

Quote:
The generated assembly shows the compiler inlines appropriately given the visibility and size of function bodies.

Do you have some sample code that doesn't optimize properly?


I havent seen any of his tests or anything and dodnt really care what hes doing it for, i just think its an really interesting problem, but passing the function pointer as a template paramater would in theory increase the number of functions that where visible to the compiler when it was electing weather to inline or not ect.

Quote:
The Boost Function Object Adapter Library provides unary_traits and binary_traits classes that I think do what you want. unary_traits::argument_type and unary_traits::result_type are the types of the argument and result of function or function object T.


They do half of what he wants, unfortunatly the problem with using them is that inorder to take the function ptr as a template paramater you already have to know the result and argument types which means they have to have already been specified as template paramaters
deffer
deffer
Quote:
Original post by Bregma
I have also never had a problem wit hthe compiler optimizing std algorithms, whether with pointers to functions or function objects (or various standard binders operating on either of the above), assuming I've enabled optimization. The generated assembly shows the compiler inlines appropriately given the visibility and size of function bodies.

Do you have some sample code that doesn't optimize properly?


I find that hard to believe.
Example may seem stupid, but IMO it shows clearly where the compiler gave up the inlining process.
void set_n(int& x, int n) { x = 0; };std::vector<int> vv(10);std::for_each(vv.begin(), vv.end(), boost::bind2nd(set_n,666));


No inlining of set_n call. Just because set_n was passed by value/pointer/whatever the compiler stopped caring.
Bregma
Bregma
Quote:
Original post by deffer
No inlining of set_n call. Just because set_n was passed by value/pointer/whatever the compiler stopped caring.


Yes, I see what you mean. If you use a pointer to a function the compiler is not allowed to inline the function body because the pointed-to-function could change dynamically at runtime. Suck an optimization is likely to do the wrong thing and is disallowed.

If you used a function object you would not experience this problem. The compiler can detect statically that a referenced function object will not change at runtime.

I gather what you're trying to do is force the compiler to perform late binding at an earlier stage so that you can implement a templated function by passing the pointer to a function. It just won't work that way. Functions are not first-cvlass objects and C++ does not have introspection (at least, not in current versions).

--smw
Stephen M. Webb
Professional Free Software Developer
iMalc
iMalc
Quote:
Original post by Bregma
Quote:
Original post by deffer
No inlining of set_n call. Just because set_n was passed by value/pointer/whatever the compiler stopped caring.


Yes, I see what you mean. If you use a pointer to a function the compiler is not allowed to inline the function body because the pointed-to-function could change dynamically at runtime. Suck an optimization is likely to do the wrong thing and is disallowed.
Hmm, I wonder if the same applies to function references?
mfawcett
mfawcett
I believe you could use result_type from the recently accepted Boost Function Types Library.

Function Types Accepted
--Michael Fawcett
deffer
deffer
Quote:
Original post by Bregma
I gather what you're trying to do is force the compiler to perform late binding at an earlier stage so that you can implement a templated function by passing the pointer to a function. It just won't work that way. Functions are not first-cvlass objects and C++ does not have introspection (at least, not in current versions).


I see what you're getting to. But I think the "problem" is much wider, it's not just about function.

Following sample represents the very same issue (only nobody really cares about it[smile]).
template< typename T, const T& Tval >struct S { /*...*/ };extern const int n;extern const float f;const int n = 5;const foat f = 6.75f;xxx(n) // should give S<int,n>xxx(f) // should give S<float,f>// What xxx stands for???


It's a problem of passing values as template arguments.
At current point, I don't believe it could be solved without macro-isms.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.