6

Keeping the old question. See below for resolution. It is probably something simple, but still. I have the following C++11 code fragment:

#include <vector>

template <typename... Ts>
struct typelist
{
};

template <typename T>
struct EventContainer
{
    typedef T Type;
    /// TODO. Ring buffer
    std::vector<T> container;

    void push(const T& t)
    {
        EventContainer<T>::container.push_back(t);
    }

    virtual ~EventContainer()
    {
    }
};


template <template <typename...> class TL>
class EventStorage:
        public EventContainer<Ts>...
{

};

class Event1
{
};

class Event2
{
};

typedef typelist<Event1,Event2> Events12;

int main()
{
    EventStorage<Events12> ev;

    return 0;
}

How can I make EventStorage inherit EventContainer templeted with each of the types in the typelist. I could do it with Loki:: library, but I want to use C++11 with variadic templates. Thank you.

Resolution1: Fixing EventStorage template template issue. This will make EventStorage, multiple inherit all EventContainer templated with each type of Ts.

template <typename...>
class EventStorage
{
};

template <typename... Ts>
class EventStorage < typelist<Ts...> >:
        public EventContainer<Ts>...
{

};

Now I have compile time error, on the following main():

int main()
{
    EventStorage<Events12> ev;
    Event1 ev1;
    ev.push(ev1);

    return 0;
}

In function ‘int main()’:
error: request for member ‘push’ is ambiguous
error: candidates are: void EventContainer<T>::push(const T&) [with T = Event2]
error: void EventContainer<T>::push(const T&) [with T = Event1]

Why the compiler is confused? After all I push with specific type. GCC 4.6.1 here.

Resolution2: As @Matthieu M. suggested I can present a forwarding method int EventStorage, but at a cost of one extra functin call:

template <typename T>
void push(const T& t)
{
    EventContainer<T>::push(t);
}

According to Alexandrescu, the compiler will optimize this forward call as long as parameters are references. Now the question is officially closed :)

Dragomir Ivanov
  • 534
  • 8
  • 19

2 Answers2

6

Is there any reason for introducing the typelist in the first place ?

template <typename T> struct Template { void push(T) {} };

template <typename... Args>
class Storage: public Template<Args>...
{
public:
  // forwarding...
  template <typename T>
  void push(T t) {
    Template<T>& me = *this;
    me.push(t);
  }
};

int main() {
  Storage< int, char > storage;
}

This works and you can typedef the whole Storage<...> bit.

EDIT: Following on comments regarding the possibility to "combine" types.

There are two solutions:

template <typename...> struct CombineStorage;

template <typename... A, typename... B>
struct CombineStorage<Storage<A...>, Storage<B...>> {
  typedef Storage<A..., B...> type;
};

Or simply provide a typelist adapter:

template <typename... Args>
class Storage<typelist<Args...>>: public Storage<Args...> {};
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • I wish I could granulate different kinds of `Event` permutations. E.g. `typedef typelist pumpEvents;` `typedef typelist displayEvents;` But as you put it that way, I would want to combine the two `typelists`, if the storage need to store them both. – Dragomir Ivanov Feb 15 '12 at 15:06
  • I edited my answer to include a `typelist` adapter and a storage combinator. – Matthieu M. Feb 15 '12 at 15:35
  • Thank you very much. I will try that, after I solve this ambiguity issue from my others answer comment. It is very strange. Isn't `push()` gots overloaded? – Dragomir Ivanov Feb 15 '12 at 15:38
  • Overload resolution with inheritance doesn't seem completely intuitive: the solution in my answer works, but does feel ugly. – Useless Feb 15 '12 at 16:14
  • @DragomirIvanov: Exact, I needed to add a forwarding method to force the disambiguation. I didn't figure how to have a `using` directive (like @Useless) with a parameter pack expansion *on the outside*. – Matthieu M. Feb 15 '12 at 16:24
  • @Useless: Yes, it works :) Thank you very much. Any recommend paper to read why we actually need this ugly hack, appreciated. Thank you, guys. – Dragomir Ivanov Feb 15 '12 at 16:31
  • 1
    Top answer here is the most concise answer I've found: essentially the compiler finds the first scope with one or more of overloads of the right name, and stops looking. Overload resolution, implicit conversions etc. are applied _only_ to this set of candidates. http://stackoverflow.com/questions/72010/c-overload-resolution – Useless Feb 15 '12 at 16:38
1

At the moment, you're never even passing a typelist instantiation to the EventStorage, just the typelist template. So currently, there is no type pack to expand.

However, you should be able to unpack the typelist with a specialization and work with type packs otherwise:

template <typename...> class EventStorage;

template <typename Head, typename... Tail> class EventStorage<Head, Tail...>
  : public EventContainer<Head>, EventStorage<Tail...>
{
  using EventContainer<Head>::push;
  using EventStorage<Tail...>::push;
};

// allows you to pass typelists for convenience
template <typename... TL> class EventStorage<typelist<TL...>>
  : public EventStorage<TL...>
{
  using EventStorage<TL...>::push;
};

The using declarations just pull all the push methods into the same overload set, which seems to work for me.

The alternative would be to add a template method (maybe just to the toplevel typelist specialization) which explicitly forwards to this->EventContainer<T>::push, but it would require an exact type match.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • Yes, I just figured it out myself. However I get ambiguity now. Assume that added/replaced your fixes I get this: `error: request for member ‘push’ is ambiguous` `error: candidates are: void EventContainer::push(const T&) [with T = Event2]` `void EventContainer::push(const T&) [with T = Event1]` Main is: ` int main() { EventStorage ev; Event1 ev1; ev.push(ev1); return 0; } ` – Dragomir Ivanov Feb 15 '12 at 15:32
  • Yeah, I didn't add the push bit, but it takes a little fiddling - editing now ... – Useless Feb 15 '12 at 15:43
  • Ah, now I see that there is a little misunderstanding. I am wantihg to inherit every `EventContainer` each templated with each type in the `typelist`. My code is actually: ` template class EventStorage { }; template class EventStorage < typelist >: public EventContainer... { }; ` Edit: I just can't format sh*t here. I am sorry:( – Dragomir Ivanov Feb 15 '12 at 15:52
  • Don't worry, the comments aren't ideal for lots of code :-) Just edit it into the end of your question, and I'll take a look. – Useless Feb 15 '12 at 16:02