4

Say I have the following code:

#include <iostream>
#include <functional>

template <int func(int)>
struct S : std::unary_function<int, int>
{
    int operator()(int x) const
    {
        return func(x);
    }
};

int foo(int x)
{
    return x;
}

int main()
{
    S<foo> s;

    std::cout << s(42) << std::endl;
}

This works okay as a way of wrapping up a function inside of a functor, which means it can be used in other templated functions (like sort, for example (assuming the functor had the right signature)). I don't want to create a functor struct for every possible return/argument type (and realistically I can't), and so I tried the following:

template <template <typename R, // Make the return type and argument type template parameters!
                    typename A> R func(A)>
struct S : std::unary_function<R, A>
{
    R operator()(A arg) const
    {
        return func(arg);
    }
};

That didn't work; it gave me compilation errors. So then I tried:

template <typename R, typename A, R func(A)>
struct S : std::unary_function<R, A>
{
    R operator()(A arg) const
    {
        return func(arg);
    }
};

Which did work. Unfortunately though, I had to change instantiations of S to be S<int, int, foo> s; instead of the nicer S<foo> s;.

Is it at all possible to templatize the function passed as a template argument such that I can do S<foo> s; and not hard code the return type and argument type of the function in S?

My google-foo hasn't been able to find a specific answer.

Edit: Now I'm wondering if this isn't possible. I just thought of "what if foo is an overloaded function?" There wouldn't be, as far as I know, a way to know which foo to use when saying S<foo> s; and thus explicitly stating return/argument type is necessary. Is this correct thinking, and does this mean that the answer to my first question is "No, it's not possible"?

Cornstalks
  • 37,137
  • 18
  • 79
  • 144

4 Answers4

3

Unfortunately, I think it's the only way to prevent necessary conversions for passing functions. But you can add function templates to help you deduce the types of (1) function args (2) function returns, like codes below:

template < typename R, typename A >
R result_of( R(A) );

template < typename R, typename A >
A arg0_of( R(A) );

Then you can use them to construct wanted function objects and let compilers do possible optimizations:

#define get_call( f ) call_t< decltype(result_of(f)), \
                              decltype(arg0_of(f)), f >()

// same as the class 'S'
template < typename R, typename A,
           R unary( A ) >
struct call_t : std::unary_function<A,R> {
    R operator()( A arg ) const {
        return unary( arg );
    }
};

Use the utility:

int neg( int arg ) {
    return -arg;
}

auto s = get_call( neg );
cout << s( 1 ) << endl;  // outputs: -1

It works too on function templates. Of course, you have to pass argument(s) to the template:

template < typename T >
T square( T arg ) {
    return arg * arg;
}

template <>
int square( int arg ) {
   cout << "square<int>()" << endl;
   return arg * arg;
}

auto sq = get_call( square<int> );
cout << sq( 12 ) << endl; // outputs: square<int>()
                          //          144

Edit: for overloaded functions, you can do conversions to tell compilers which version you wanna invoke:

int cube( int arg ) {
    return arg * arg * arg;
}

float cube( float arg ) {
    return arg * arg * arg;
}

typedef float (*chosen)( float );
auto cu = get_call( (chosen)cube );
cout << showpoint << cu( 4 ) << endl; // outputs: 64.0000
  • +1 Liked the use of decltype. However, don't call it *template function*, call it *function template*. โ€“ Nawaz Jan 22 '13 at 15:16
2

You seem to want to have a non-type template template parameter. However, the only legal syntax for template template parameters is template < template-parameters > class. ("A template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression." ยง 14.3.3)

You could create a templated class whose constructor argument was a function pointer, but I'm guessing that you're worried that will create an indirect function call.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Yeah, you're right about my worry about the indirect function call... this code is a template metaprogramming library for super computers. Thanks for the quote from the standard! โ€“ Cornstalks Jan 24 '13 at 07:16
2

That is not possible. It is the same problem in principle as the following one: you wish to write just A<100> where A is defined as:

template<T N>
struct A {};

Given N is 100, T turns out to be int. Fine. That is deducible by human mind, but not by the compilers even if they be 100% conformant to the C++11 Standard. I've exactly the same problem here:

--

So the alternative solution I think is this:

template <typename R, typename A>
struct S : std::unary_function<R, A>
{
    typedef R (*Fun)(A);
    Fun func;
    S(Fun f) : func(f) {}
    R operator()(A arg) const
    {
        return func(arg);
    }
};

And then define MakeS function as:

template<typename R, typename A>
S<R,A> MakeS(R (*fun)(A)) 
{  
    return S<R,A>(fun); 
}

Which you can use it as:

auto s = MakeS(foo);

Or, simply this:

S<int,int> s(foo);

The downside with this alternative is that the function foo doesn't have any chance to be inlined now.

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
0

Does this work for you?

It may not be as nice as S<foo> but keeps the arguments as 1 at the point of instantiation.

int f(int) { return 0; }


template<class R, class A> struct S
{
    typedef R(*FTYPE)(A);
    typedef R RET;
    typedef A ARG;
};

template<class R, class A> S<R, A> FW(R(f)(A));

template<class T> struct F  : std::unary_function<typename T::RET, typename T::ARG>
{

};

int main()
{
    F<decltype(FW(f))> o;
}
Chubsdad
  • 24,777
  • 4
  • 73
  • 129
  • `o` doesn't bind to any function. `F` only knows about the *type* of the function, not the *function* itself. โ€“ Nawaz Jan 22 '13 at 08:08