Reputation: 63
I have such template functions:
template<class R> list<R> f(const boost::function<R()>&);
template<class R, class A0> list<R> f(const boost::function<R(T0)>&, list<A0>);
template<class R, class A0, class A1> list<R> f(const boost::function<R(T0)>&, list<A0>, list<A1>);
To run one of them i need to write for example:
int one() { return 1; }
int inc(int x) { return x + 1; }
list<int> l;
f<int>(one);
f<int, int>(inc, l);
And my goal is to just write:
f(one);
f(inc, l);
I heard that this is possible by some kind of template signature specialization, but I can't figure out how.
Upvotes: 4
Views: 214
Reputation: 10720
Without C++11, you can't get away from specifying a return type of a function.
template<typename R, typename F>
R bar(F func) {
return func();
}
bar<int>(foo);
With the new C++11 features you can though.
template<typename F>
auto baz(F func) -> decltype(func()) {
return func();
}
baz(foo);
You can template the function/functor as a parameter, instead of trying to specify that it has to be a boost::function.
void zero() {cout << "zero" << endl;}
void one(int a) {cout << "one" << endl;}
void two(int a, int b) {cout << "two" << endl;}
template<typename F>
void f(const F &func) {
func();
}
template<typename F, typename T0>
void f(const F &func, T0 t0) {
func(t0);
}
template<typename F, typename T0, typename T1>
void f(const F &func, T0 t0, T1 t1) {
func(t0, t1);
}
This lets you pass in the function pointer rather simply.
f(zero);
f(one, 1);
f(two, 1, 2);
If you need to actually use functions or bind you can pass that in to the same interface.
// without specifying the function
f(boost::bind(zero));
f(boost::bind(one, _1), 1);
f(boost::bind(two, _1, _2), 1, 2);
// or by specifying the object
boost::function<void()> f0 = boost::bind(zero);
boost::function<void(int)> f1 = boost::bind(one, _1);
boost::function<void(int,int)> f2 = boost::bind(two, _1, _2);
f(f0);
f(f1, 1);
f(f2, 1, 2);
As well as with a functor, which is typical for passing in strict weak ordering behavior to the standard containers.
struct zoobies {
void operator()() const {}
};
f(zoobies());
It doesn't have to check the type of what you pass in to it, only that it satisfies the interface. This is one of the reasons C++ templates are typically much more powerful than generics in other languages.
And for completeness... If you actually did want to restrict it to boost::function, here is an example.
template<typename T>
void p(const boost::function<T> &func) {
func();
}
template<typename T, typename A0>
void p(const boost::function<T> &func, A0 a0) {
func(a0);
}
boost::function<void()> f0(zero);
p(f0);
boost::function<void(int)> f1(one, _1);
p(f1, 1);
Update:
void foo() {cout << "zero" << endl;}
void foo(int a) {cout << "one" << endl;}
void foo(int a, int b) {cout << "two" << endl;}
boost::bind works out of the box, with this, although the raw function pointers have more of a problem. foo is ambiguous there.
f( (void(*)()) foo );
f( (void(*)(int)) foo, 1 );
f( (void(*)(int,int)) foo, 1, 2);
If you fully specify the function pointer then it works, though that isn't what anyone wants to do.
With boost::bind as evidence, You should be able to determine the arity from the calling convention of f
. If I get some time today I'll play with it.
Upvotes: 2