Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411588 Posts in 69386 Topics- by 58443 Members - Latest Member: Mansreign

May 06, 2024, 05:01:45 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Passing functions as template parameters
Pages: [1] 2
Print
Author Topic: Passing functions as template parameters  (Read 6002 times)
Crimsontide
Level 5
*****


View Profile
« on: May 27, 2010, 03:17:25 PM »

With the new C++0x additions filtering down I've been playing around alot more with template metaprogramming.  The combination of auto, decltype, and lambda functions add alot of interesting possibilities.  There's still one thing I can't seem to be able to do, and that is pass an arbitrary function as a template parameter.

of course I can pass a function at run time like:
Code:
template <typename T> void Call (const T& f) { f(); }
// elsewhere...
Call(SomeFunc);

if I know the type of function I want, I can do something like:
Code:
template <void (&f) (void)> void Call () { f(); }
// elsewhere...
Call<SomeFunc>();

But the only way I can pass an arbitrary function via a template parameter would be something like:
Code:
template <typename T, const T& f> void Call () { f(); }
// elsewhere...
Call<void (void),SomeFunc>();

meaning you would have to supply the function definition prior to the actual function.  For functions that are more than just 1 or 2 parameters (or more complex parameters like embedded iterators) this becomes more than just a chore, and is error prone.

So the question is, is there any possible way to pass a function as a template parameter without having to specify the function type by hand?  Something like:
Code:
Call<SomeFunc>();   // not valid C++ code


Logged
oahda
Level 10
*****



View Profile
« Reply #1 on: May 27, 2010, 04:54:23 PM »

Why would you want, or need, to do that?
Logged

Crimsontide
Level 5
*****


View Profile
« Reply #2 on: May 27, 2010, 05:06:13 PM »

Well there was a number of situations where I wanted to do it, but had to work around it.  In this particular case I couldn't.  Its the last piece of a simple recursive decent parser.
Logged
muku
Level 10
*****


View Profile
« Reply #3 on: May 27, 2010, 05:16:00 PM »

How would you call the function if you don't know how many and what types of parameters it has?
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #4 on: May 27, 2010, 05:26:28 PM »

So the question is, is there any possible way to pass a function as a template parameter without having to specify the function type by hand?  Something like:
Code:
Call<SomeFunc>();   // not valid C++ code

If I understand what you're doing, I believe the answer is no.

One solution is to use classes with overloaded () operators, and use those as "function wrappers."

Code:
class Funk_1
{
public:
    void operator () ()
    {
        // Do stuff.
    }
};

class Funk_2
{
public:
    void operator () ()
    {
        // Do other stuff.
    }
};

template <typename Type>
void Call()
{
    Type()();
}

int main()
{
    Call<Funk_1>();
    Call<Funk_2>();
}
Logged



What would John Carmack do?
Crimsontide
Level 5
*****


View Profile
« Reply #5 on: May 27, 2010, 08:41:33 PM »

So the question is, is there any possible way to pass a function as a template parameter without having to specify the function type by hand?  Something like:
Code:
Call<SomeFunc>();   // not valid C++ code

If I understand what you're doing, I believe the answer is no.

One solution is to use classes with overloaded () operators, and use those as "function wrappers."

Code:
class Funk_1
{
public:
    void operator () ()
    {
        // Do stuff.
    }
};

class Funk_2
{
public:
    void operator () ()
    {
        // Do other stuff.
    }
};

template <typename Type>
void Call()
{
    Type()();
}

int main()
{
    Call<Funk_1>();
    Call<Funk_2>();
}


Ya I tried that too.  You can't use operator() as it can't be static, but you can just use a normal static function (like Function() or whatever).  Problem with that is you can't get the type of the function you are intending to call.
Logged
muku
Level 10
*****


View Profile
« Reply #6 on: May 28, 2010, 12:49:49 AM »

Maybe it would help if you could explain the concrete problem you're trying to solve.
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #7 on: May 28, 2010, 02:23:15 AM »

Why would you want, or need, to do that?

That is the single most annoying reply you can get to a question. Don't ever reply this. Lips Sealed
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
muku
Level 10
*****


View Profile
« Reply #8 on: May 28, 2010, 03:04:20 AM »

Why would you want, or need, to do that?

That is the single most annoying reply you can get to a question. Don't ever reply this. Lips Sealed

It's true though that often people decide on some particular way to approach a problem, and when they realize it won't really fly, instead of reconsidering their approach they ask about the specific micro-problem that stopped them in their tracks instead of giving enough information to suggest a more suitable design.

But, yeah, it was definitely not the nicest way to put this.
Logged
westquote
Level 0
**


I make games in Philly. How rare!


View Profile WWW
« Reply #9 on: May 28, 2010, 05:41:40 AM »

While I am not well-informed as to the constraints implicit in your use cases, I suspect that any attempt write code that calls a function of a truly variable type will be impossible.  It would be possible to store it, retrieve it, verify that it is the same type as something else, etc..., so you might be able to use this feature to create an opaque variant-like storage mechanism.  However, if you ever try to dispatch to it, you will have to know something about its parameters.

For instance, by calling f() in your function, you are implicitly assuming that it can take in zero parameters.  If you tried to use it on a function that must take in a single int parameter, you would need to create a version of the function: void Call(int i){f(i);}.  This is all to say that the problem you're running into might be clouding the larger problem of automatic variadic parameter forwarding.  In the pre-C++0x spec, the solution I've used is macro-iterated generation of partially-specialized templates for each possible number of function parameters, such that I can pass a function in as a parameter, and it will deduce the correct specialization, which provides a version of my Call() function that can correctly forward on the arguments.  I also believe it's still possible to do it (albeit less-elegantly) using full specialization, instead of partial specialization.  In the new spec, I wonder if a combination of variadic templates, decltype, and trailing return types could enable this to be done without instantiating a specialization of every possible number of parameters.  Do you know of such a technique in the new spec?

I don't have time this morning, but if you're interested, I can write up a little something on macro iteration and/or partial-specialization on function signatures.  Let me know whether that would be valuable to you.  Cheers!  Smiley
Logged

Twitter: @westquote - Webpage: Final Form Games
oahda
Level 10
*****



View Profile
« Reply #10 on: May 28, 2010, 06:54:10 AM »

Why would you want, or need, to do that?

That is the single most annoying reply you can get to a question. Don't ever reply this. Lips Sealed
I'm just wondering, since I use C++ too.
I can't come up with any situation where this would be needed, so that's why I want to know.
Some new insight is never wrong.
Logged

Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #11 on: May 28, 2010, 07:09:14 AM »

Why do you want to pass the function as a template parameter, as opposed to a real parameter?  Is it for optimization purposes?  That's the only rationale I can come up with.

If that's the case, I'd reckon that making Call inline and passing constant function pointers at runtime would effectively get you the same performance, as the constant function address could be propagated through the inline function.

I'm pretty sure that variadic templates would handle all the parameter concerns, but since I'm not still not boned up on the syntax for those, don't take this as gospel.  I believe it would look something like this:

Code:
template <typename FuncType, typename Args...>
inline void Call(FuncType funk, Args... args)
{
    funk(args...);
}
Logged



What would John Carmack do?
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #12 on: May 28, 2010, 09:54:59 AM »

Answer: function pointer typedefs, no C++0x needed:

Code:
#include <iostream>

using namespace std;


typedef void (*pF)(void);


template<pF FUNC>
struct C
{
    void doStuff() { FUNC(); }
};

void A() { cout << "I am A!\n"; }
void B() { cout << "I am B!\n"; }

int main()
{
    C<A> ca;
    C<B> cb;

    ca.doStuff(); //I am A!
    cb.doStuff(); //I am B!

    return 0;
}
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #13 on: May 28, 2010, 10:06:56 AM »

Answer: function pointer typedefs, no C++0x needed:

Code:
#include <iostream>

using namespace std;


typedef void (*pF)(void);


template<pF FUNC>
struct C
{
    void doStuff() { FUNC(); }
};

void A() { cout << "I am A!\n"; }
void B() { cout << "I am B!\n"; }

int main()
{
    C<A> ca;
    C<B> cb;

    ca.doStuff(); //I am A!
    cb.doStuff(); //I am B!

    return 0;
}

typedefs really don't solve anything, you're just introducing short-hand, typedefs are semantically null.

This is the same solution as my original example (with operator () ), you've just changed what's being wrapped.
Logged



What would John Carmack do?
Klaim
Level 10
*****



View Profile WWW
« Reply #14 on: May 28, 2010, 10:53:16 AM »

Why don't you simply use std::function instead?

What you're trying to achieve have no sense, it's like if you simply were using std::function in code (be it templated or not).

Logged

Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #15 on: May 28, 2010, 11:16:59 AM »

Why don't you simply use std::function instead?

What you're trying to achieve have no sense, it's like if you simply were using std::function in code (be it templated or not).



std::function doesn't arrive until C++0x, right?  Or is it in TR1?
Logged



What would John Carmack do?
Crimsontide
Level 5
*****


View Profile
« Reply #16 on: May 28, 2010, 01:54:13 PM »

Actually I mentioned I was using C++0x (at least parts of it) in the OP, but no worries if you missed it.  And ya if I wanted run-time linkage I could use std::function, or boost::function, or one of many other options.

After doing more reading, I'm pretty sure what I'm looking for can't be done.  But I'll try to explain it in a bit more detail.

Many times I want to wrap a function in another function.  Either to adjust parameters, modify data, ensure something was called, ect...  Doing this at run-time is easy, but it also has overhead.  For example, imagine you have something like this:

Code:
float FuncA (int i) { /* do stuff and return something */ }
float FuncB (int i) { return FuncA(i+1); }

Its a trivial example granted, but wrapping a function to add/adjust functionality happens all the time.  Most compilers (GCC, intel, MS) will inline the call to FuncA in FuncB (with the appropriate compiler optimizations turned on, even without the inline keyword).

To do the equivalent with say boost::function (and likewise std::function):

Code:
float FuncB (const boost::function<float (int)>& f, int i) { return f(i+1); }

works pretty much the same, but won't necessarily be inlined (haven't tested it out, but would be highly unlikely that it could inline it... I probably should test that sometime).  Same problem with a function pointer:

Code:
float FuncB (float (*f) (int), int i) { return f(i+1); }

Now you can do:

Code:
template <float (&f) (int)> float FuncB (int i) { return f(i+1); }

and this gives you 50% of what I'm looking for.  You get a complile-time wrapper with no more overhead than a hand coded wrapper would give you.  Which is awesome, but it only works if you know the type of the function you want to wrap.

Say I want the type of a function (to adjust/work with parameters, ect...).  I can do:

Code:
template <typename T> float FuncB (const T& f, int i) { return f(i+1); }

and now I have T, which is the type of a function, and I can work with it, but its run time, not compile time.

And there-in lies the rub.  I can statically link a function OR I can get its type, but I can't do both.  What I need is something like:

Code:
template <typename T, const T& f> float FuncB (int i) { return f(i+1); }

Except having to supply the function definition is not only tedious, but error prone for complex types.  The closest I've been able to get is by wrapping a function in a class (as has been mentioned above) and using decltype like:

Code:
template <typename T> void FuncB (int i) { 
   const auto f = T::FuncA;
   typedef decltype(f) TF;   // get function type
   return f(i+1);
   }

This gets me 90% of the way there, problem is I can't get the function type, till the wrapper function is declared, so I can't define things like return values.

There's also:

Code:
template <typename T, const T& f> float FuncB (int i) { return f(i+1); }
// elsewhere...
FuncB<decltype(FuncA),FuncA>(5);

Which seems about the best option at this point.  A little tedious to write, but it covers the rest of my wish-list.

I just thought I'd post the question as sometimes I make silly mistakes and/or forget obvious things, and I thought maybe in this case there was something I missed or forgot ; )
Logged
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #17 on: May 28, 2010, 02:44:33 PM »

That first part cannot be emphasized enough. An optimizing compiler should be good enough to inline a constant function pointer, making the "runtime cost" of the straight forward approach zero.

I.e. inline
Code:
template<class F>
inline void call(F f){f();}

void foo();

int main(){
call(&foo);
}
to
Code:
int main()
{
  foo();
}
Logged
Klaim
Level 10
*****



View Profile WWW
« Reply #18 on: May 28, 2010, 04:23:16 PM »

Why don't you simply use std::function instead?

What you're trying to achieve have no sense, it's like if you simply were using std::function in code (be it templated or not).



std::function doesn't arrive until C++0x, right?  Or is it in TR1?

It's in std::tr1 namespace in VS2008 SP1, and available in GCC too.
In VS2010 and GCC with C++0x activated it's in std namespace.

I've been using it all the time for years, it's available in Boost!
Logged

Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #19 on: May 29, 2010, 07:31:06 AM »

Did Average's suggestion about variadic templates work?
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic