6

I need to pass a function to an operator. Any unary function having correct arg type. Return type can be anything. Because this is library code, I can not wrap it or cast f to specific overload (outside of operator*). Function takes operator* 1st arg as it own argument. Artificial example below compiles and returns correct results. But it has hardcoded int return type—to make this example compile.

#include <tuple>
#include <iostream>
using namespace std;

template<typename T>
int operator* (T x,  int& (*f)(T&) ) {
    return (*f)(x);
};

int main() {
    tuple<int,int>  tpl(42,43);
    cout << tpl * get<0>;
}

Is it possible to make operator* to accept f with arbitrary return type?

UPDATE - GCC bug? Code:

#include <tuple>

template<typename T, typename U> 
U operator* (T x,  U& (*f)(T&) ) {  
    return (*f)(x);
}; 

int main() {
    std::tuple<int,int>  tpl(42,43);
    return   tpl * std::get<0,int,int>;
}  

Compiles and runs correctly with gcc462 and 453 but is reject with gcc471 and 480. So it is possible GCC regression bug. I've submitted bug report: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54111

EDIT I've changed example to use tuple as arg - it was possible trivially deduce return type in previous example.

EDIT2 Many people could not understand what is needed, so I've changed call function to operator* to make example more real.

Leonid Volnitsky
  • 8,854
  • 5
  • 38
  • 53
  • [My question](http://stackoverflow.com/questions/9625526/check-at-compile-time-if-template-argument-is-void) might help you a bit here. It allows me to call a wrapped variation via `someVar = wrap (funcName, arg1, arg2, arg3);`. It handles a `void` return type too. – chris Jul 28 '12 at 02:26
  • 1
    Generally speaking type deduction and overloading are somewhat at odds in C++. You've come right to the limit -- more generalization and then there's not enough information to establish what overload to pick. – Luc Danton Jul 28 '12 at 03:20
  • The edit changes the question completely, you should create a different question for that, and BTW, it cannot be resolved, as `get<0>` is not a function, `std::get` is a variadic template from which all but the first argument can be inferred during use, but, without providing the arguments to the function you have to provide all the template arguments manually, at which point you need no inference, as you can obtain it from the arguments that you are manually passing to the `get` template... – David Rodríguez - dribeas Jul 28 '12 at 03:59
  • @DavidRodríguez - no, `get<0>` is enough. Example BTW compiles and runs and returns correct value. `get<0>` arguments are specified in `call` signature. In previous example, `f` was template also. – Leonid Volnitsky Jul 28 '12 at 04:26

3 Answers3

4

Yes, if this is what you mean:

template<typename T, typename F> 
auto call (T x, F f) -> decltype(f(x)) {  
    return (f)(x); 
}

There are actually a lot of ways to do that.

Xeo
  • 129,499
  • 52
  • 291
  • 397
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
2

You should be able to do this:

template<typename T,typename U>
U call (T x, U (*f)(T) ) {
      return (*f)(x);
};
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • It compiles (and you got +1 from me). But it was not exactly what I wanted. My fault - bad example. I've changed it to use tuple as arg and now your approach won't work. – Leonid Volnitsky Jul 28 '12 at 03:14
  • @Xeo: No - get<0> is enough. The not needed - can be deduced. Cannot wrap. The lvalue/rvalue I can handle with overloads. – Leonid Volnitsky Jul 28 '12 at 03:20
  • @Xeo: You underestimate me :) In `call` signature there are `f` args types specified. This is enough to extract specific everload. Example compiles and runs. – Leonid Volnitsky Jul 28 '12 at 03:30
  • @Leonid: TIL: C++'s template argument deduction is more powerful than I thought. I completely forgot about contextual information for template argument deduction. [Small test case](http://liveworkspace.org/code/513cc26f1e98fae8b744932ad2314bcc). I'm still wondering, why can't you just accept an `F f` templated parameter? – Xeo Jul 28 '12 at 03:36
  • @Xeo: `F f` won't extract overload --> will not compile because of ambiguity. – Leonid Volnitsky Jul 28 '12 at 03:43
  • @Leonid: Really, I don't think you have much choice here. Either have the user wrap it or provide a simple wrapper (like my former example with `lazy_get`) and simply take an `F f`. Function pointers are hideous anyways, they completely inhibit inlining, as opposed to function objects. – Xeo Jul 28 '12 at 03:45
2

As an answer to your updated question:

as discussed by @DavidRodríguez, get<0> is not enough, nor the syntatically correct &get<0>. What you need is &get<0,int,int>. Follows your example, it would be:

#include <tuple>
using namespace std;

template<typename T, typename U>
U call (T x, U (*f)(T&) ) {
      return (*f)(x);
};

int main() {
    tuple<int,int>  tpl(42,43);
    call(tpl, &get<0,int,int>);
    return 0;
}

During normal use of std::get<>(), the int,int part is deduced automatically. But in your situation you need to provide it, since there is no parameters. One workaround is a custom get template function:

#include <tuple>
using namespace std;

template <size_t I, typename T>
auto myGet(T& tpl) -> decltype(get<I>(tpl))
{
    return get<I>(tpl);
}

template<typename T, typename U>
U call (T x, U (*f)(T&) ) {
      return (*f)(x);
};


int main() {
    tuple<int,int>  tpl(42,43);
    auto get0 = &myGet<0, decltype(tpl)>;
    call(tpl, get0);

//  call(tpl, &myGet<0, decltype(tpl)>); // all in one line, do not work
    return 0;
}
user2k5
  • 827
  • 1
  • 7
  • 9
  • If it was not enough, than my example would not compile/run. Your 1st code example was rejected by gcc48 – Leonid Volnitsky Jul 28 '12 at 06:01
  • @Leonid, but your example cannot do what you want: accept arbitrary return type of `f`. I'm using gcc 4.6.3 and the first code is accepted. Anyway, how about the second code? – user2k5 Jul 28 '12 at 06:17
  • I didn't tried 2nd code - as I explained in my question: I can not wrap or cast `f`. I don't have gcc463, but I've tried with gcc453 and gcc471. 1st - compiles&run correctly (you are getting +1), 2nd - rejected. I will file bug report with gcc. – Leonid Volnitsky Jul 28 '12 at 06:31
  • I am filing bug report: does my code compiles and runs correctly with gcc463? – Leonid Volnitsky Jul 28 '12 at 07:01
  • @LeonidVolnitsky: the code under **UPDATE - GCC bug**: it compiles ok with gcc 463. – user2k5 Jul 28 '12 at 07:20