0

I am trying to insert a function into a map but I want to check it first so I would like to overload assignment operation for std::function, is this possible?

I try to overload the assignment operation, so if something other than expected is assigned, the assignment operator function should wrap it in the expected function and return it.

#include <iostream>
#include <map>
#include <functional>

class MyClass{
    public:
    std::map<int, std::map<int, std::function<void(int,int)>>> events;
    std::function<void(int,int)>& on(int type, int id){ return events[type][id]; };
    template<typename T> std::function<void(int,int)>& operator= (T&& fn){
        std::wcout << L"assigning correct function\n";
        return [&](int x, int y){
            if(typeid(fn)==typeid(std::function<void(int,std::wstring)>)) fn(x, L"two");
        };
    }
};

int main(int argc, char **argv)
{
    MyClass obj;
    obj.on(1,2) = [](int x, int y){ std::wcout << L"int " << x << L" " << y << std::endl; };  //this works but it's not calling the overload operator
    obj.on(1,2) = [](int x, std::wstring y){ std::wcout << L"string " << x << L" " << y << std::endl; }; //I need this to work too
    obj.events[1][2](2,3);
    return 0;
}

Output:

test.cpp:23:14: error: no match for 'operator=' (operand types are 'std::function<void(int, int)>' and 'main(int, char**)::<lambda(int, std::__cxx11::wstring)>')
obj.on(1,2) = [](int x, std::wstring y){ std::wcout << L"string " << x << L" " << y << std::endl; };
         ^
shuji
  • 7,369
  • 7
  • 34
  • 49
  • Well, you're trying to assign a function that takes `(int, std::wstring)` to a `std::function` that expects `(int, int)`, and `int` is not implicitly convertible to `wstring. – Barry Sep 17 '15 at 20:14
  • @Barry that's why I try to overload the assignment operation, so if something other than expected is assigned, the assignment operator function should wrap it in the expected function and return it. – shuji Sep 17 '15 at 20:19
  • Is this a real problem? Because there are probably better ways to achieve the same thing. Otherwise, if you really want to do this it's the = function of std::function<...> you need to overload. You are overloading the operator= function of MyClass instead ...I think! :) So (if you really want to do this) you'd need a specalization of std::function<> with a new assignment operator overload. But errrr... don't ;-) – JCx Sep 17 '15 at 20:26
  • not really, I will update. The problem resides in that I need to return the right function depending on one parameter value. – shuji Sep 17 '15 at 20:30
  • Ok - if you really want to do this how about a function you use for assignment that you can simply overload with different types of std::function. obj.on(1,1) = convertToIntIntFunction(someFunction); At least it'll be obvious what's going on... ? – JCx Sep 17 '15 at 20:33
  • Yes that could do the job, I could also just add the function inside the on function, I was just expecting to have a clean assignment operation so it didnt turn into a callback hell or some function needed to be wrapped all the time – shuji Sep 17 '15 at 20:47

1 Answers1

2

Sounds like what you need is a proxy class. The problem is, when you return a std::function<..>& from on(), you end up with a std::function. You can't overwrite that class's operator=, which is what I think you're trying to do. Instead, you're overwriting MyClass::operator= - which is a function you're never actually calling.

Instead, return a proxy whose assignment you can control. Something like this:

struct Proxy {
    std::function<void(int, int)>& f;
};

Proxy on(int type, int id){ return {events[type][id]}; };

And then we can provide special overloads for Proxy::operator=. The "valid, correct type" case:

template <typename F,
          std::enable_if_t<std::is_assignable<std::function<void(int, int)>&, F&&>::value>* = nullptr>
Proxy& operator=(F&& func) {
    f = std::forward<F>(func);
    return *this;
}

and the wstring case:

template <typename F,
          std::enable_if_t<std::is_assignable<std::function<void(int, std::wstring)>&, F&&>::value>* = nullptr>
Proxy& operator=(F&& func) {
    std::wcout << L"assigning correct function\n";
    f = [func = std::forward<F>(func)](int x, int ) {
        func(x, L"two");
    };
    return *this;
}

With that, your original main() will compile and do what you expect.

Barry
  • 286,269
  • 29
  • 621
  • 977