4

I have some code which seems unambiguous to me, but gcc4.7 is choking on it:

#include <iostream>
#include <tuple>

using namespace std;

// Container for mixins
template<template<typename> class... Mixins>
struct Mix : Mixins<Mix<Mixins...>>... {
  typedef tuple<Mixins<Mix<Mixins...>>...> types;
};

// Outer layer extracts the type tuple from the argument
template<typename T>
struct InnerCombiner {
  typedef typename InnerCombiner<typename T::types>::type type;
};

// Typedef type to be a new mix of the inner mixins of the MixedMixins
template<typename... MixedMixins>
struct InnerCombiner<tuple<MixedMixins...>> {
  // This line is the problem. The compiler doesn't seem to be able to make sense
  // of the reference to the inner mixin template template classes
  typedef Mix<MixedMixins::InnerMixin...> type;
};

template<typename Mixed>
struct A {
  template<typename MixedInner>
  struct InnerMixin {
    void foo() { cout << "foo() loves you!" << endl; };
  };
};

template<typename Mixed>
struct B {
  template<typename MixedInner>
  struct InnerMixin {
    void bar() { cout << "bar() loves you!" << endl; };
  };
};

// I'm going to write out the type I expect ic to have. Oh god, it's so nasty:
// Mix<
//   A<Mix<A,B>>::InnerMixin<Mix<A<Mix<A,B>>::InnerMixin,B<Mix<A,B>>::InnerMixin>,
//   B<Mix<A,B>>::InnerMixin<Mix<A<Mix<A,B>>::InnerMixin,B<Mix<A,B>>::InnerMixin>
// >


int main() {
  InnerCombiner<Mix<A,B>>::type ic;

  ic.bar(); // Not working.
}

Is there something wrong with accessing InnerMixins this way? It seemed pretty reasonable when I wrote it :)

Casey Rodarmor
  • 14,878
  • 5
  • 30
  • 33
  • Unrelated to your problem but is that meant to be `InnerCombiner::types>::type`, or maybe the `InnerCombiner` specialization is meant to match `Mix` or a generic template template parameter rather than `std::tuple`. – Luc Danton Mar 20 '12 at 07:44
  • I thought that that was correct, since T is intended to be some kind of Mix<...>, and mix has a ::types, so the unspecialized InnerCombiner extracts the types to apply to the partially specialized InnerCombiner. Will that work? You are right though, that it would be more elegant to match a Mix<...>. I think I would still have the same problem though. – Casey Rodarmor Mar 20 '12 at 07:57
  • PS 253 Answers, and no questions?! Don't you ever get stumped? :) – Casey Rodarmor Mar 20 '12 at 07:57
  • Yes, that will work. I was stumped (speaking of) by a GCC error message that found an `int` member when there isn't any in the program. – Luc Danton Mar 20 '12 at 08:02
  • @LucDanton Yeah, I noticed that too. I think that's possibly a compiler strategy to try to continue after an error, so that it can give more errors and you can fix them all at once. – Casey Rodarmor Mar 20 '12 at 08:07

2 Answers2

4

I can make it work on clang 3.0 by specifying InnerMixin a template:

typedef Mix<MixedMixins::template InnerMixin...> type;
//                       ^^^^^^^^

but it still fails on g++ 4.8, with

3.cpp:23:52: error: parameter packs not expanded with ‘...’:
3.cpp:23:52: note:         ‘MixedMixins’
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Darn. Thanks for the answer anyways. Do you happen to know the provision of the standard which explains why that works? – Casey Rodarmor Mar 20 '12 at 08:09
  • 1
    @rodarmor [This question](http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) and its answers has the references. – Luc Danton Mar 20 '12 at 08:15
  • 1
    @Luc unfortunately the Standard does not currently require "::template" for a template template-argument. I sent an issue report about that last week, and I hope for it to be added to the next issues list . – Johannes Schaub - litb Mar 24 '12 at 14:01
1

There's a type/value mismatch, this should at the very least be MixedMixins::template InnerMixin.... However GCC still rejects that and I found no way of cajoling it. Unfortunately I'm having a hard time proving that such a pack expansion is in fact valid. Hopefully someone more versed in the grammar could answer that point.


In a more 'lateral' approach, have you considered ditching template template parameters altogether? Not only could that ease the pain of syntax, but you can still 'rebind' the template parameters of a template specialization:

// We accept two types, a template specialization and
// a sequence of would be template parameters.
template<typename Specialization, typename T>
struct rebind;

template<
    template<typename...> class Template
    , typename... Old
    template<typename...> class Sequence
    , typename... T
>
struct rebind<Template<Old...>, Sequence<T...>> {
    using type = Template<T...>;
};

template<typename S, typename... T>
using Rebind = typename rebind<S, T...>::type;

E.g. Rebind<std::vector<int>, std::tuple<double, std::allocator<double>> is std:vector<double>. Combine that with a parameters_of/ParametersOf utility to extract the template parameters of a specialization into e.g. an std::tuple.

As a disclaimer I haven't used those techniques myself for long but I've already appreciated how I can limit the template template parameters pain points to a few, centralized spots of my code.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114