Как проверить кортеж С++ 11 во время выполнения для возможного применения функции

У меня есть функция, применяющая данную функцию f к заданному std::tuple t, как показано ниже, в которой, если t имеет неправильный размер, генерируется сообщение об ошибке времени компиляции. Мне было интересно, можно ли написать это по-другому, чтобы при неправильном размере кортежа вызов функции не происходил (возможно, с использованием enable_if или что-то в этом роде) и генерировалось сообщение об ошибке времени выполнения (вместо ошибки времени компиляции сообщение).

Заранее спасибо.

#include <tuple>
#include <iostream>
#include <functional>

template<int ...> struct seq {};

template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};

template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

template <typename R, typename Tp, typename ...FArgs> 
struct t_app_aux {
  template<int ...S>
  R static callFunc(std::function<R (FArgs...)> f,Tp t,seq<S...>) {
    return f(std::get<S>(t) ...);
  }
};

template <typename R, typename Tp, typename ...FArgs>
R t_app(std::function<R (FArgs...)> f, Tp t) {
  static_assert(std::tuple_size<Tp>::value == sizeof...(FArgs), "type error: t_app wrong arity"); //wrong size generates a compile error, how about runtime check?
  return t_app_aux<R, Tp, FArgs...>::callFunc(f,t,typename gens<sizeof...(FArgs)>::type());
}

int main(void)
{
  std::tuple<int, float, double> t = std::make_tuple(1, 1.2, 5);
  std::function<double (int,float,double)> foo = [](int x, float y, double z) {
    return x + y + z;
  };
  std::cout <<  t_app(foo,t) << std::endl;
}

-- Обновлять --

Немного мотивации и объяснения того, что я имею в виду. Это может быть политически некорректно, но скажем, я хочу сравнить входной кортеж с фиксированным кортежем, определенным внутри функции шаблона compare_me (скажем, внутренний кортеж make_tuple(2,3) ). Если бы я мог подавить ошибку времени компиляции, то я мог бы вернуть false на compare_me(make_tuple(1,2,3)) или compare_me(make_tuple(1,2)) и true только на make_tuple(2,3). На практике тот же метод можно использовать для написания шаблона eq для сравнения двух кортежей потенциально разных размеров и возврата false, если их размеры не совпадают.

Несоответствие размера можно рассматривать как ошибку компиляции. И я согласен с тем, что настоящий провал должен быть обнаружен как можно раньше. Но я предполагаю, что бывают моменты, когда можно проявить снисходительность или пересмотреть то, что является настоящим провалом. Это верно, особенно если учесть, что существуют такие функции, как std::tuple_cat, которые фактически могут дать вашей функции кортеж различных размеров.


person tinlyx    schedule 22.01.2014    source источник
comment
Почему вы хотите обменять сбой времени компиляции на сбой времени выполнения? Ошибиться раньше — своего рода девиз C++. Если вы лучше опишите проблему, которую хотите решить, может быть лучший ответ.   -  person Jeffery Thomas    schedule 22.01.2014
comment
Как создать кортеж размера времени выполнения?   -  person leewz    schedule 23.01.2014
comment
Я думаю, что в этом случае нет необходимости генерировать кортежи размера времени выполнения. template<typename Tp> int f(Tp t) { return tuple_size<Tp>::value;} уже может обрабатывать кортежи разных размеров. Я спрашиваю, можно ли, например, сравнить два кортежа разных размеров без возникновения ошибки компиляции.   -  person tinlyx    schedule 23.01.2014
comment
Вы также можете сделать if (true) f(make_tuple()); else f(make_tuple(1,2));. Я думаю, что до тех пор, пока не используется возвращаемый тип или повторное использование переменных, кортежи разных размеров можно использовать вместе.   -  person tinlyx    schedule 23.01.2014


Ответы (1)


Используйте enable_if и условие в static_assert для условного включения текущей функции только при совпадении размеров. Создайте другую функцию, которая активируется только тогда, когда условие ложно, и создайте исключение из этой функции.

template <typename R, typename Tp, typename ...FArgs>
auto t_app(std::function<R (FArgs...)> f, Tp t)
    -> typename std::enable_if<std::tuple_size<Tp>::value == sizeof...(FArgs), R>::type {
//  static_assert(std::tuple_size<Tp>::value == sizeof...(FArgs), "type error: t_app wrong arity"); //wrong size generates a compile error, how about runtime check?
  return t_app_aux<R, Tp, FArgs...>::callFunc(f,t,typename gens<sizeof...(FArgs)>::type());
}

template <typename R, typename Tp, typename ...FArgs>
auto t_app(std::function<R (FArgs...)>, Tp)
    -> typename std::enable_if<std::tuple_size<Tp>::value != sizeof...(FArgs), R>::type {
  throw std::runtime_error("type error: t_app wrong arity");
}

Демо

person Praetorian    schedule 22.01.2014
comment
Должен сказать, немного странно, что вы хотите обменять ошибку времени компиляции на ошибку времени выполнения. - person Praetorian; 22.01.2014
comment
Цель C++ состоит не в том, чтобы заставить вашу программу соответствовать: она состоит в том, чтобы заставить вашу программу не компилироваться, если она неверна настолько, насколько это разумно, при этом не препятствуя компиляции правильных программ и не замедляя их. Есть причины использовать вышеуказанную методику, но они достаточно редки. - person Yakk - Adam Nevraumont; 22.01.2014
comment
@Praetorian Большое спасибо за ваш ответ. Возможно ли, чтобы две функции возвращали один и тот же тип R? Например, могу ли я сделать так, чтобы функция возвращала true, если их размеры совпадают, и false, если нет? Это то, что я хотел иметь в сигнатуре std::function. - person tinlyx; 23.01.2014