5

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?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288

2 Answers2

2

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)

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
1

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;
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288