tags:

views:

93

answers:

1

(Related to C++0x, How do I expand a tuple into variadic template function arguments?.)

The following code (see below) is taken from this discussion. The objective is to apply a function to a tuple. I simplified the template parameters and modified the code to allow for a return value of generic type.

While the original code compiles fine, when I try to compile the modified code with GCC 4.4.3,

g++ -std=c++0x main.cc -o main

GCC reports an internal compiler error (ICE) with the following message:

main.cc: In function ‘int main()’:
main.cc:53: internal compiler error: in tsubst_copy, at cp/pt.c:10077
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-4.4/README.Bugs> for instructions.

Question: Is the code correct? or is the ICE triggered by illegal code?

// file: main.cc
#include <tuple>

// Recursive case
template<unsigned int N>
struct Apply_aux
{
  template<typename F, typename T, typename... X>
  static auto apply(F f, const T& t, X... x)
    -> decltype(Apply_aux<N-1>::apply(f, t, std::get<N-1>(t), x...))
  {
    return Apply_aux<N-1>::apply(f, t, std::get<N-1>(t), x...);
  }
};

// Terminal case
template<>
struct Apply_aux<0>
{
  template<typename F, typename T, typename... X>
  static auto apply(F f, const T&, X... x) -> decltype(f(x...))
  {
    return f(x...);
  }
};

// Actual apply function
template<typename F, typename T>
auto apply(F f, const T& t)
  -> decltype(Apply_aux<std::tuple_size<T>::value>::apply(f, t))
{
  return Apply_aux<std::tuple_size<T>::value>::apply(f, t);
}

// Testing
#include <string>
#include <iostream>

int f(int p1, double p2, std::string p3)
{
  std::cout << "int=" << p1 << ", double=" << p2 << ", string=" << p3 << std::endl;
  return 1;
}

int g(int p1, std::string p2)
{
  std::cout << "int=" << p1 << ", string=" << p2 << std::endl;
  return 2;
}

int main()
{
  std::tuple<int, double, char const*> tup(1, 2.0, "xxx");
  std::cout << apply(f, tup) << std::endl;
  std::cout << apply(g, std::make_tuple(4, "yyy")) << std::endl;
}

Remark: If I hardcode the return type in the recursive case (see code), then everything is fine. That is, substituting this snippet for the recursive case does not trigger the ICE:

// Recursive case (hardcoded return type)
template<unsigned int N>
struct Apply_aux
{
  template<typename F, typename T, typename... X>
  static int apply(F f, const T& t, X... x)
  {
    return Apply_aux<N-1>::apply(f, t, std::get<N-1>(t), x...);
  }
};

Alas, this is an incomplete solution to the original problem.

+1  A: 

I tried you code on g++4.6. It does not compile due to missing implementation. However, one way to achieve generality would be to wrap the free standing function in a std::function wrapper and use result_type typedef like below.

template<typename F, typename T>
typename F::result_type apply(F f, const T& t)
{
  ...
}
int f(int p1, double p2, std::string p3) 
{
  std::cout << "int=" << p1 << ", double=" << p2 << ", string=" << p3 << std::endl;
  return 1;
}
int main()
{
  std::tuple<int, double, char const*> tup(1, 2.0, "xxx");
  std::function<int (int, double, char const *)> func = &f; 
  std::cout << apply(func, tup) << std::endl;

}
Sumant
Elegant solution. Unfortunately, I wasn't able to get it to work in GCC 4.4.3. I'm not sure why. I'll post here if I find anything new. Cheers
Marc H.