1

I have functions

void addOnHover(std::function<void(Button&, HoverState)>& func);
void addOnHover(void(*fptr)(Button&, HoverState));

Button inherits ButtonBase, which is interface class and I want to pass

void hoverOver(game::ButtonBase& button, game::HoverState state)
{
    static int i = 0;
    if (state == game::HoverState::STATE_ENTER)
        std::cout << "entered " <<    button.getLabel().getText().getString().toAnsiString() << " " << ++i << "\n";
    else if (state == game::HoverState::STATE_LEAVE)
        std::cout << "left " << button.getLabel().getText().getString().toAnsiString() << " " << ++i << "\n";
    else
        std::cout << "inside " << button.getLabel().getText().getString().toAnsiString() << " " << ++i << "\n";
}

lets say b is of type Button

but if I do b.addOnHover(hoverOver); it will not compile(some funky compiler error, as always with this kind of staff).

I can make it work if I do b.addOnHover(std::function<void(game::Button&, game::HoverState)>(hoverOver); or if I change the hoverOver from taking game::ButtonBase& to game::Button&, but I wonder if it could be done without such verbosity(std::function...) and while keeping the argument as game::ButtonBase& because there may be more Button classes in future

as per Bolov request, here is compiler error when passing hoverOver

1>Source.cpp(58): error C2664: 'void game::Button::addOnHover(std::function<_Fty> &)' : cannot convert parameter 1 from 'void (__cdecl *)(game::ButtonBase &,game::HoverState)' to 'std::function<_Fty> &'
1>          with
1>          [
1>              _Fty=void (game::Button &,game::HoverState)
1>          ]

edit:

One possible solution, even tho still pretty verbose is to add

template <class T, class C, class Q>
std::function<void(T&, C)> wrap(Q ptr)
{
    return std::function<void(T&, C)>(ptr);
}

and call b.addOnHover(game::wrap<game::Button, game::HoverState>(hoverOver); but Im not sure if it will function correctly yet

Creris
  • 1,128
  • 9
  • 20
  • 1
    `std::function` is implicitly constructible from function pointers as well. The overload is kinda redundant. – StoryTeller - Unslander Monica Feb 02 '14 at 17:04
  • 1
    Why not `std::function`, then? – jrok Feb 02 '14 at 17:10
  • Why dont you use Interface class name in addHover ? – Arunmu Feb 02 '14 at 17:11
  • "some funky compiler error, as always with this kind of staff". Right, nevertheless it is an error message and it does provide you with information about the source of your problem. You shouldn't disregard completely an error just because it has a very long and convoluted message. At least post the error for us to see. – bolov Feb 02 '14 at 17:20
  • make `void addOnHover(const std::function& func);` (add const) testing to see if this is the problem – bolov Feb 02 '14 at 17:26

2 Answers2

2

One problem that you have is that an non-const lvalue reference cannot be bound to an rvalue.

make

void addOnHover(const std::function<void(Button&, HoverState)>& func);

or (preferably):

void addOnHover(std::function<void(Button&, HoverState)> func);

With your version the parameter func is a non-const lvalue, and when you call it like this b.addOnHover(hoverOver) hoverOver is a pointer to function so a temporary object is created of type std::func (rvalue) which, as I have said, cannot be bound to a non-const lvalue reference.

Btw, i figured this out when I saw the error message, so it is important to try to go through the error.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • is `hoverOver` a class member rather than a free function? – bolov Feb 02 '14 at 17:41
  • its normal function just above main, and that would work, but Im trying to pass function taking parent class to the member function, which expects itself(I pass function with argument `ButtonBase` but the member function addOnHover expects function with argument `Button`) and I see no other way than either change the `hoverOver` to take `Button` or make `addOnHover` to expect function with `ButtonBase` param – Creris Feb 02 '14 at 17:47
2

There are two potential issues I can see.

1) The type of the 'parameter' (i.e. Button) void addOnHover(std::function<void(Button&, HoverState)>& func); is more specific than the value you give ButtonBase in void hoverOver(game::ButtonBase& button, game::HoverState state)...

This is as if you are assigning a ptr to the parent class to a ptr to a child class.

If you had used plain values, a ButtonBase& parameter should accept Button&, but not the other way around. In other words, I think you should have

void addOnHover(std::function<void(ButtonBase&, HoverState)>& func);

and

void hoverOver(game::Button& button, game::HoverState state)... (or void hoverOver(game::ButtonBase& button, game::HoverState state)...)

2) even if the types do match, std::function does not automatically accept conversion from a function pointer or lambda of the same type. It's a pain of using std::function as it is now in c++11. You need to assign the function to a std::function object first or call the constructor as you did. This is, unfortunately, unlike the case where you can have std::string accept const char*.

I guess what your std::function usage or the wrapper does is a type cast. So compiler accepts it because you explicitly told the compiler the cast should be OK. It doesn't mean the types are actually compatible.

Regarding the second issue, I have asked a related question about how to let std::function accept a function(plain, lambda etc) of the corresponding type: How to make C++11 functions taking function<> parameters accept lambdas automatically . The answer I got most is that I shouldn't use std::function or expect to use it that way.

Community
  • 1
  • 1
thor
  • 21,418
  • 31
  • 87
  • 173
  • funny enough, it worked :D I have decided to change the hoverOver to take game::Button& (and had to change the reference to normal in the addOnHover), thanks for the help tho – Creris Feb 02 '14 at 18:03