0

THIRD EDIT: Today I received email from VS support stating that it's know issue, which has been fixed in VS2017. So, I will stick to my workaround for the time being.

I'm working on event system and I wanted to use some metaprogramming to save on typing. Here's the code:

struct foo { using type = int; };
struct bar { using type = char; };

template<typename... Types> struct TypeList {};


//This and getScript are simplified, in reality the involve messier templates...
template<typename ReturnType>
struct ReqBuilder
{
    using proxy = ReturnType(*)(void*, bool);
//  ...
};
template<typename scriptType>
using getScript = ReqBuilder<scriptType>;


template<typename List, template<typename> typename Wrap> struct WrapTypeList_impl {};
template<typename...Ts, template<typename> typename Wrap> struct WrapTypeList_impl<TypeList<Ts...>, Wrap>
{
    using type = TypeList<Wrap< Ts>...>;
};
template<typename List, template<typename> typename Wrap>
using WrapTypeList = typename WrapTypeList_impl<List, Wrap>::type;

//Use typedef
using list1 = TypeList<getScript<foo>, getScript<bar>>;
//Write it manually
using list2 = TypeList<ReqBuilder<foo>, ReqBuilder<bar>>;
//Use wrapper to wrap each element of typelist
using list3 = WrapTypeList<TypeList<foo, bar>, getScript>;

Goal is to have all three list result in the same TypeList. This code is simplified, in reality getScript does more work and it's actually useful. Same for ReqBuilder which contains more typedefs. So, list3 should be much easier to write than either list1 or list2.

There are more getScript, ReqBuilder-like classes and flattening of nested typelists. Which allows to easily write list of all possible events even with complex types.

This code above works - all lists are equal. Problem is when specialize (and I want to) on nested type:

template<typename scriptType>
using getScript = ReqBuilder<typename scriptType::type>;

Then I get following error atWrapTypeList_impl::type=... line:

Error C2938 'getScript<unknown-type>' : Failed to specialize alias template     

I really have no idea what that even means, but it probably has something to do with pack expansion, because when I add this specialization:

template<typename A,typename B, template<typename> typename Wrap>
struct WrapTypeList_impl<TypeList<A,B>, Wrap>
{
    using type = TypeList<Wrap<A>,Wrap<B>>;
};

Then it works even with nested types. So, does anyone have any idea how to get this working please?

I'm using Visual studio 2015 Comunnity edition. And even more puzzling thing is that using intellisense - hovering over lists shows they resolved in same TypeList, even std::is_same<list1,list3>::value shows true on hover. But compiler for some reason disagrees.

EDIT: ADDED FULL CODE THAT CAUSES SAID ERROR

#include <algorithm>

struct foo { using type = int; };
struct bar { using type = char; };

template<typename... Types> struct TypeList {};


//This and getScript are simplified, in reality the involve messier templates...
template<typename ReturnType>
struct ReqBuilder
{
    using proxy = ReturnType(*)(void*, bool);
//  ...
};
template<typename scriptType>
using getScript = ReqBuilder<typename scriptType::type>;


template<typename List, template<typename> typename Wrap> struct WrapTypeList_impl {};
template<typename...Ts, template<typename> typename Wrap> struct WrapTypeList_impl<TypeList<Ts...>, Wrap>
{
    using type = TypeList<Wrap<Ts>...>;
};
template<typename List, template<typename> typename Wrap>
using WrapTypeList = typename WrapTypeList_impl<List, Wrap>::type;


using list1 = TypeList<getScript<foo>, getScript<bar>>;
using list2 = TypeList<ReqBuilder<typename foo::type>, ReqBuilder<typename bar::type>>;
using list3 = WrapTypeList<TypeList<foo, bar>, getScript>;

int main()
{
    constexpr bool A = std::is_same<list1, list2>::value;
    constexpr bool B = std::is_same<list1, list3>::value;
    constexpr bool C = std::is_same<list2, list3>::value;

    static_assert(A, "list1 != list2");
    static_assert(B, "list1 != list3");
    static_assert(C, "list2 != list3");
}

Erorrs:

1>  Main.cpp
1>Main.cpp(23): error C2938: 'getScript<unknown-type>' : Failed to specialize alias template
1>  Main.cpp(31): note: see reference to class template instantiation 'WrapTypeList_impl<TypeList<foo,bar>,getScript>' being compiled
1>Main.cpp(23): error C3546: '...': there are no parameter packs available to expand
1>Main.cpp(36): error C3203: 'TypeList': unspecialized class template can't be used as a template argument for template parameter '_Ty2', expected a real type
1>Main.cpp(37): error C3203: 'TypeList': unspecialized class template can't be used as a template argument for template parameter '_Ty2', expected a real type
1>Main.cpp(40): error C2338: list1 != list3
1>Main.cpp(41): error C2338: list2 != list3

Adding said A,B specialization for Wrapper after variadic one solves it.

SECOND EDIT - FOUND WORKAROUND

So, original problem still exists, but after bit of trial and error, I tried to replace dependent type with separate struct, which seems to solve the problem:

template<typename scriptType>
struct getScript_impl
{
    using type = ReqBuilder<typename scriptType::type>;
};
template<typename scriptType>
using getScript = typename getScript_impl<scriptType>::type;

/* REPLACING THIS WITH CODE ABOVE
template<typename scriptType>
using getScript = ReqBuilder<typename scriptType::type>;
*/

Full code, which works in VS2015 for me: http://ideone.com/kGT4qM

As I briefly stated in my comment, I think the issue is that alias template using dependent type as another template argument does not instantiate that template or something like that.(I'm not a template wizard, I really just poke at them, and see how they respond).

I've found few links that are related to this issue, but didn't have time to look at them yet(link1; link2; link3).

Quimby
  • 17,735
  • 4
  • 35
  • 55
  • Please provide code after modification that actually causes an error. Simply adding or replacing `getScript` block does not seem to give C2938. – user7860670 Apr 14 '17 at 12:48
  • @VTT Sorry about that, added full code and all erorrs that it causes. – Quimby Apr 14 '17 at 14:08
  • And intented goal is for me to just write list3 which automatically gets expanded into longer list1 which expands into yet more complicated list2. – Quimby Apr 14 '17 at 14:10
  • The first noticeable thing is that in the expression `using list3 = WrapTypeList, getScript>;` getScript is used without template arguments list and it does not have any default argument either. – user7860670 Apr 14 '17 at 14:17
  • I fixed mistake in code, in WrapTypeList_impl should use Wrap instead of getScript directly. Yes, I want to fill it inside with arguments from Typelist, so I don't know what should I put inside. – Quimby Apr 14 '17 at 14:29
  • @Quimby ... do you still experience issues? The full code as given at the bottom of the question [does not seem to produce any error](http://ideone.com/C42Hy6) at all. – Daniel Jour Apr 14 '17 at 22:36
  • @DanielJour Partly yes, but I've found workaround, will edit post shortly. I've created new project and copy pasted your code and it does generate same errors as I listed above. And as I said, hovering on lists show they resolved to equal types(Intellisense?), but compiler does not see it that way. Which is really weird to me. I found this [link](http://stackoverflow.com/questions/21395786/template-aliases-and-dependent-names) which mentions "Class templates and function templates are instantiated, but alias templates are simply substituted". Which might be related to my problem, see my edit. – Quimby Apr 15 '17 at 16:54
  • Not reproducible with gcc or clang. – n. m. could be an AI Apr 15 '17 at 17:26
  • Ok, reported it using VS2015 report a problem tool, will update this post if I get any answers. Meanwhile, thank you for your help. – Quimby Apr 15 '17 at 19:02
  • 1
    I ran into the same issue with VS2015. Thanks for posting this. Your workaround probably saved me days of headache. – Kenneth Moreland Jul 17 '19 at 21:55

0 Answers0