0

I am having some troubles understanding why the following code cannot compile

#include <iostream>
#include <typeinfo>

#define PRINT_FUNC() {std::cout << __PRETTY_FUNCTION__ << std::endl;}

struct Obj {
    Obj(){PRINT_FUNC();}
    int run (float f, char *c) {
        PRINT_FUNC();
        return 0;
    }

    int fly () {
        PRINT_FUNC();
        return 0;
    }
};

template <typename OBJ, typename R, typename ... Args>
void call_obj_func (OBJ &&o, R(OBJ::*fn)(Args...), Args ... args) {
    PRINT_FUNC();
    (o.*fn)(args...);
}

int main () {
    Obj o;
    call_obj_func(o, &Obj::fly);
}

For the function call_obj_func I expected the type of OBJ to be used for BOTH rvlaue and lvalue types. However when calling with a lvalue type, the compiler complains that there is ambuigity of using the types: Obj and Obj&

This means that the comiler isnt sure wheter to use a copy of the obj or the reference to the obj.

I am sure there is some syntax error as I would like the function call_obj_func to be compiled with both lvalue and rvalue types.

My assumption is the member function pointer as the syntax (Obj&::*fn) and (Obj::*fn) might have different semantics. (Though I cannot find the differences anywhere).

m.s.
  • 16,063
  • 7
  • 53
  • 88
  • What is the actual error message? – Aaron McDaid Aug 08 '15 at 07:18
  • 1
    You could just declare `o` as `OBJ o`, not as `OBJ &&O`. By adding `&&` you are not going to get perfect forwarding unless you do perfect forwarding properly, which includes replacing `(o.*fn)(args...);` with `(std::forward(o).*fn)(args...);`. Anyway, perfect forwarding won't fix your compiler error - that's a template deduction issue. But removing the `&&` should fix it (and you will still be able to accept lvalues and rvalues) – Aaron McDaid Aug 08 '15 at 07:26

1 Answers1

0

You may write

template <typename OBJ, typename Method, typename ... Args>
void call_obj_func (OBJ &&o, const Method& fn, Args&& ... args) {
    PRINT_FUNC();
    (std::forward<OBJ>(o).*fn)(std::forward<Args>(args)...);
}

Live Demo

As currently, you have conflict with type deduction (Obj& vs Obj, and you may have similar issues with args)

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    @PiotrSkotnicki: Thanks typo fixed (sad that it was in part which doesn't produce error in live demo) – Jarod42 Aug 08 '15 at 11:11
  • Your demo is very good, but I felt like I could add to it. Let me know what you think. (I used PasteBin because that demo site said something about paying to prevent people from editing it.) https://pastebin.com/x1UDDCe6. ... I made a function that accepts just a member fptr and returns a 'Functor' that does the job of your `call_obj_func`, but it saves the fptr and exposes `operator()` so callers only need to know / see the basic interface unless they expand it. I did so with 'update' to demo policies. These could compose, but data structures of f'tors might be more expressive anyway. – John P Dec 29 '17 at 02:27
  • I checked my work a little more closely and it appears that I either haven't worked out the kinks with external linkage or my design won't allow it. I'm still experimenting, but if I forget to come back here or something, you won't be able to use the state of the implementer ('Obj' in both our examples) like I'd hoped. You can still use its methods, just follow the same rules as using member functions on null pointers. If I do continue this I'll make a Github repo. – John P Dec 29 '17 at 06:52
  • @JohnP: Your version works with internal construct `obj` (You have dangling pointer BTW `IMP && actor;` should be `IMP actor;`) whereas OP might provide different existing object.A simpler interface would even be `template void call_f(F&& f) {PRINT_FUNC(); std::forward(f)();}` with usage `call_f([](){o.fly();});`. – Jarod42 Dec 29 '17 at 10:38
  • Is that all it was? I think that was one of the first things I reverted while troubleshooting. I had added `IMP&&` because of a red herring. ... I think I know where/why I diverged - I was basically building a universal adapter, along the lines of dependency inversion. If you came across a penguin for the first time, you wouldn't know if it could run or fly at all - but you know you can startle it to get it moving, and it'll sort itself out without knowing you, either. Why `call_f` when `call_f` doesn't save or change `f`, you know you can `o.fly()`, etc.? Self closure maybe? – John P Dec 29 '17 at 11:49