2

Objectif of this code is to create a wrap around diffent template design (an implementation with always the same handler work), but with this try I have the following error "error: request for member 'pushEvent' is ambiguous" I don't understand (detail of the error on the bottom of the message)

template<typename T>
struct EventHandler {
    EventHandler() {}

    inline void pushEvent(T & msg) {
    printf("pushEvent %s", typeid(T).name());
        q_.push(msg);
}

protected:
    std::queue<T> q_;
};

The wrapper of deferend template (some method will be add to handle the different type of events

template<typename... EventHandler>
    struct _EventsHandler : EventHandler... {

};

// usage sample : We create the type that will instantiate the event manager

 using EventHandlerType = EventsHandler<EventHandler<int>,EventHandler<float>,EventHandler<char>>;

int main(void) {
    EventHandlerType test;

    int msp = 1;
    test.pushEvent(msp);
}

I don't understand the ambiguous error as msp type is int and should give information to solve the ambiguity ?

error: request for member 'pushEvent' is ambiguous
  test.pushEvent(msp);
       ^
note: candidates are: void _EventHandler<T>::pushEvent(T&) [with T = char]
     inline void pushEvent(T & msg) {
                 ^
note: void _EventHandler<T>::pushEvent(T&) [with T = float]
note: void _EventHandler<T>::pushEvent(T&) [with T = int]
note: void _EventHandler<T>::pushEvent(T&) [with T = char]

I know that such solution can be handled differently, but I want to understand the WHY of this error, so please don't mark this question as "already answered" if the answer is "there is an other way to solve your problem"

O'Neil
  • 3,790
  • 4
  • 16
  • 30
zadigus
  • 23
  • 5
  • I guess that's because it cannot decide whether the argument should be treated as char or as int. – Ashalynd May 30 '14 at 13:39
  • No in the first attempt I use diffetent structures as T, and I get the same error (the structure parameter work well for other template) – zadigus May 30 '14 at 13:43
  • 1
    https://ideone.com/K25GXh reproduces the error without template. – Jarod42 May 30 '14 at 13:50
  • It's not even related to `char`/`int` decision problem but rather to multiple inheritance and having the same method name in various ancestors . – Johan May 30 '14 at 13:59
  • 2
    http://stackoverflow.com/questions/5368862/why-do-multiple-inherited-functions-with-same-name-but-different-signatures-not – Dark Falcon May 30 '14 at 13:59
  • Thanks for your link and details, it help me understanding the root cause outside of the template – zadigus May 30 '14 at 14:09

2 Answers2

6

Before overload resolution, name lookup is performed, which has its own set of rules. It has to find an unambiguous name and there isn't one in your case.

You may use the following instead (https://ideone.com/ChvQ4q)

template<typename... Ts>
struct _EventsHandler;

template<typename T>
struct _EventsHandler<T> : T
{
    using T::pushEvent;
};

template<typename T, typename... Ts>
struct _EventsHandler<T, Ts...> : T, _EventsHandler<Ts...>
{
    using T::pushEvent;
    using _EventsHandler<Ts...>::pushEvent;
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thanks a lot it works but it doesn't solve the trick, I'm trying to turn around. My goal was to be able to use a different template for the deferent event – zadigus May 30 '14 at 14:49
  • If I take your solution, my objectif was : using EventHandlerType = _EventsHandler, EventHandler2, EventHandler3>; – zadigus May 30 '14 at 14:51
  • You can already use other type than `EventHandler`. I will replace the template parameter to an other name to avoid confusion with previous class `EventHandler`. – Jarod42 May 30 '14 at 15:06
  • Thanks a lot, I fork your sample to show how to use 2 different handler http://ideone.com/rXeL52 – zadigus May 30 '14 at 15:23
3

This works :

#include <typeinfo>
#include <queue>
#include <cstdio>

template<typename T>
struct EventHandler {
    EventHandler() {}

    void pushEvent(T & msg) {
    printf("pushEvent %s", typeid(T).name());
        q_.push(msg);
}

protected:
    std::queue<T> q_;
};

template<typename... EventHandler>
    struct _EventsHandler : EventHandler... {
        template <typename T>
        void pushEvent(T & msg) {
            // Choose appropriate superclass
            ::EventHandler<T>::pushEvent(msg);
        }
};

using EventHandlerType = _EventsHandler<EventHandler<int>,EventHandler<float>,EventHandler<char>>;

int main(int, char**) {
    EventHandlerType test;

    int msp = 1;
    test.pushEvent(msp);
}

I lack sufficient template voodoo to explain this one, but no doubt someone will be able to (and probably nail it with the exact paragraph from the standard)

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 2
    It's not template voodoo, it's multi heritance voodoo. This line `::EventHandler::pushEvent(msg);` does the trick fairly well. Nice ! – Johan May 30 '14 at 14:02
  • That assume that template parameter `EventHandler` is a/(one of) `::EventHandler` – Jarod42 May 30 '14 at 14:05
  • And that's what I actually meant... Coffee time ! Glad to have helped nonetheless :) Edit: @Jarod: compile-time type safety for free, what are you complaining about ? :D – Quentin May 30 '14 at 14:05
  • Thanks for the answer,it did not solve my probelm as I want to be able to change the temple "EventHandler", the source code name lead to misunderstanding – zadigus May 30 '14 at 14:39
  • As I took care to alter your code the least possible, I reckon this template function I added will survive renaming. Do keep in mind though that ::EventHandler designates the global namespace type, not the template parameter. – Quentin May 30 '14 at 14:45