September 04, 2016

Sigs: Ambiguous Member Functions

I recently updated my sigs project (simple thread-safe signal/slot C++ library) with support for ambiguous member functions by use of the sigs::Use<>::overloadOf():

template <typename... Args>
struct Use {
  template <typename Cls, typename Ret>
  static auto overloadOf(Ret (Cls::*MembFunc)(Args...)) {
    return MembFunc;
  }
};

It makes use of variadic templates and auto return type deduction so the compiler must be C++14 compliant.

To illustrate why it was needed consider this: Sometimes there are several overloads for a given function and then it’s not enough to just specify &Class::functionName because the compiler does not know which overload to choose.

The Ambiguous class has two overloads of foo:

class Ambiguous {
public:
  void foo(int i, int j) { std::cout << "Ambiguous::foo(int, int)\n"; }

  void foo(int i, float j) { std::cout << "Ambiguous::foo(int, float)\n"; }
};

Without the new construct the following would result in compilation failure:

sigs::Signal<void(int, int)> s;

Ambiguous amb;
s.connect(&amb, &Ambiguous::foo); // <-- Will fail!

The error would look something similar to this:

test.cc:147:7: error: no matching member function for call to 'connect'
    s.connect(&amb, &Ambiguous::foo);
    ~~^~~~~~~
./sigs.h:144:16: note: candidate template ignored: couldn't infer template argument 'MembFunc'
    Connection connect(Instance *instance, MembFunc Instance::*mf) {

To fix it we must use sigs::Use<>::overloadOf():

s.connect(&amb, sigs::Use<int, int>::overloadOf(&Ambiguous::foo));
s(42, 48);

/* Prints:
Ambiguous::foo(int, int)
*/

Without changing the signal we can also connect the second overload foo(int, float):

// This one only works because int can be coerced into float!
s.connect(&amb, sigs::Use<int, float>::overloadOf(&Ambiguous::foo));
s(12, 34);

/* Prints:
Ambiguous::foo(int, int)
Ambiguous::foo(int, float)
*/

Posted with : Sigs, C++, C++14