Working Around a Visual Studio Internal Compiler Error With Nested Templated Variables

618 Views Asked by At

I'm trying to write code that will let me index into the parameter types of a function:

template <typename R, typename... ARGS>
R function_return(R(*)(ARGS...));

template <typename R, typename... ARGS>
std::tuple<ARGS...> function_parameters(R(*)(ARGS...));

template <int I, typename T>
using get_type = typename std::conditional_t<(I < 0), std::tuple_element<static_cast<int>(std::tuple_size_v<T>) + I, T>, std::tuple_element<I, T>>::type;

template <int I, typename T>
using parameter_type = get_type<I, decltype(function_parameters(std::declval<T>()))>;

Live Example (ICE under VS) Live Example (working on GCC)

But when I try to use this on I get an internal compiler error:

fatal error C1001: An internal error has occurred in the compiler.

Is there another way that I can do this which might work around the internal compiler error?

2

There are 2 best solutions below

0
Jonathan Mee On BEST ANSWER

As mentioned here it seems like struggles with templated using statements being passed to each other. (I'm seeing the internal compiler error on 15.6.7; as mentioned here this may have been fixed by patches.)

I've been able to work around it by capturing all the functionality in a single using statement:

template <typename R, typename... ARGS>
R function_return(R(*)(ARGS...));

template <typename R, typename... ARGS>
std::tuple<ARGS...> function_parameters(R(*)(ARGS...));

template <int I, typename T, typename X = decltype(function_parameters(std::declval<T>()))>
using parameter_type = typename std::conditional_t<(I < 0), std::tuple_element<static_cast<int>(std::tuple_size_v<X>) + I, X>, std::tuple_element<I, X>>::type;
1
Angew is no longer proud of SO On

It probably depends on the exact (sub)version of VS2017, as mine does not produce an ICE on the code. However, the code is still problematic, as it can potentially instantiate std::tuple_element<2147483647, T> or something similar. You need to make sure only the correct branch is ever evaluated. Replace your definition of get_type with this:

template <int I, typename T, bool negative = (I < 0)>
struct get_type_impl;

template <int I, typename T>
struct get_type_impl<I, T, true>
{
  using type = typename std::tuple_element<static_cast<int>(std::tuple_size<T>::value) + I, T>::type;
};

template <int I, typename T>
struct get_type_impl<I, T, false>
{
  using type = typename std::tuple_element<I, T>::type;
};

template <int I, typename T>
using get_type = typename get_type_impl<I, T>::type;

This works for me on my VS 2017 (cl version 19.12)