C ++ 0x: перебор кортежа с функцией - PullRequest
3 голосов
/ 23 июля 2010

У меня есть функция с именем _push, которая может обрабатывать различные параметры, включая кортежи, и, как предполагается, возвращает количество отправленных элементов.

Например, _push(5) должен нажать «5» в стеке ( стек lua ​​) и вернуть 1 (потому что было передано одно значение), в то время как _push(std::make_tuple(5, "hello")) должно нажать «5» и 'привет' и вернись 2.

Я не могу просто заменить его на _push(5, "hello"), потому что иногда я использую _push(foo()) и хочу разрешить foo() вернуть кортеж.

В любом случае мне не удается заставить его работать с кортежами:

template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, typename std::enable_if<(N >= 1)>::type* = nullptr) {
 return _push<Args...,N-1>(t) + _push(std::get<N-1>(t));
}

template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, typename std::enable_if<(N == 0)>::type* = nullptr) {
 return 0;
}

Допустим, вы хотите нажать tuple<int,bool>. Вот как я ожидаю, что это сработает:

  • _push<{int,bool}, 2> называется (первое определение)
  • _push<{int,bool}, 1> называется (первое определение)
  • _push<{int,bool}, 0> называется (второе определение)

Однако с g ++ 4.5 (единственным имеющимся у меня компилятором, который поддерживает шаблоны с переменными числами), я получаю сообщение об ошибке _push<Args...,N-1>(t) (строка 3), в котором говорится, что не удалось найти соответствующую функцию для вызова (без каких-либо дополнительных подробностей). Я пытался без "...", но я получаю еще одну ошибку о том, что пакет параметров не расширен.

Как я могу это исправить?

PS: я знаю, что вы можете сделать это, используя структуру шаблона (на самом деле это то, что я делал раньше), но я хотел бы знать, как это сделать с помощью функции

PS 2: PS2 решена, спасибо GMan

Ответы [ 6 ]

6 голосов
/ 23 июля 2010

У меня нет компилятора для тестирования всего этого, поэтому вам придется сообщать о любых проблемах.

Следующее должно позволить вам выполнять итерации по кортежу, вызывающему функцию.Это основано на вашей логике, с небольшими изменениями.(N - это std::size_t, это первый параметр, который позволяет выводить ArgsFunc) при последующих вызовах, он просто вызывает некоторую функцию вместо выполнения определенной задачи).Ничего особенного:

namespace detail
{
    // just to keep things concise and readable
    #define ENABLE_IF(x) typename std::enable_if<(x)>::type

    // recursive case
    template <std::size_t N, typename... Args, typename Func>
    ENABLE_IF(N >= 1) iterate(const std::tuple<Args...>& pTuple, Func& pFunc)
    {
        pFunc(std::get<N - 1>(pTuple));

        iterate<N - 1>(pTuple, pFunc);
    }

    // base case
    template <std::size_t N, typename... Args, typename Func>
    ENABLE_IF(N == 0) iterate(const std::tuple<Args...>&, Func&)
    {
        // done
    }
}

// iterate tuple
template <typename... Args, typename Func>
Func iterate(const std::tuple<Args...>& pTuple, Func pFunc)
{
    detail::iterate<sizeof...(Args)>(pTuple, pFunc);

    return pFunc;
}

Если предположить, что все работает, то у вас просто есть:

struct push_lua_stack
{
    // constructor taking reference to stack to push onto
    // initialize count to 0, etc....

    template <typename T>
    void operator()(const T& pX)
    {
        // push pX onto lua stack
        ++count;
    }

    std::size_t count;
};

И наконец:

std::size_t pushCount = iterate(someTuple, push_lua_stack()).count;

Дайте мне знать, если это всеимеет смысл.


Поскольку по какой-то причине вы действительно серьезно относитесь к структурам, просто создайте такую ​​функцию:

template <typename T>
void push_lua(const T& pX)
{
    // push pX onto lua stack
}

И измените все, чтобы вызывать эту функцию специально:

namespace detail
{
    // just to keep things concise and readable
    #define ENABLE_IF(x) std::enable_if<(x)>::type* = nullptr

    // recursive case
    template <std::size_t N, typename... Args>
    typename ENABLE_IF(N >= 1) iterate(const std::tuple<Args...>& pTuple)
    {
        // specific function instead of generic function
        push_lua(std::get<N - 1>(pTuple));

        iterate<N - 1>(pTuple);
    }

    // base case
    template <std::size_t N, typename... Args, typename Func>
    typename ENABLE_IF(N == 0) iterate(const std::tuple<Args...>&, Func&)
    {
        // done
    }
}

// iterate tuple
template <typename... Args>
void _push(const std::tuple<Args...>& pTuple)
{
    detail::iterate<sizeof...(Args)>(pTuple);
}

Не знаю, почему бы вам избежать универсальной функциональности или быть против структур.


О, как бы хороши были полиморфные лямбды.Отбросьте утилиту push_lua_stack класса и просто напишите:

std::size_t count = 0;

iterate(someTuple, [&](auto pX)
                    {
                        // push onto lua stack
                        ++count;
                    });

Ну да ладно.

1 голос
/ 21 сентября 2010

Вместо того, чтобы одна функция делала две разные вещи, разделите задачи:

_push(value, ...); // using variadic templates for 1 to N values
_push_seq(sequence); // always requires a sequence, never a value

Тогда проблема просто больше не существует для _push! У вас нет двусмысленности относительно того, нужно ли выдвигать один элемент, содержащий несколько значений (я не знаком с Lua, но я знаю, что у него есть основной класс контейнера), или передавать несколько элементов из одной последовательности.

Переименование функций может быть полезным:

_append(value, ...); // _push(value, ...) above
_extend(sequence); // _push(sequence) above

Для сравнения рассмотрим, как std :: vector всегда использует push_back для одного элемента (_append) и вставку для нескольких элементов (_extend); он не пытается смешать два понятия.

1 голос
/ 23 июля 2010

Я решил проблему с некоторыми взломами. Вот код:

template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N >= 1)>::type* = nullptr) {
    return _push(t, static_cast<std::integral_constant<int,N-1>*>(nullptr)) + _push(std::get<N-1>(t));
}
template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N == 0)>::type* = nullptr) {
    return 0;
}

Не стесняйтесь писать, если вы найдете лучший способ

0 голосов
/ 19 февраля 2011

Редактировать общий для каждого

template<size_t N>
struct for_each_impl
{
  template<typename Func, typename Tuple>
  void operator()(Func func, Tuple const& arg)
  {
    for_each_impl<N-1>()(func, arg );
    return func( std::get<N-1>( arg ) );
  }
};

template<>
struct for_each_impl<1>
{
  template<typename Func, typename Tuple>
  void operator()(Func func, Tuple const& arg)
  {
    func( std::get<0>( arg ) );
  }
};

template<typename Func, typename ... Args>
void for_each( Func func, std::tuple<Args...>const& tup )
{
  for_each_impl< sizeof...(Args)>()( func, tup );
}

Пример использования

struct printer {
    ostream& out;
    explicit printer( ostream& out=std::cout ) : out(out) { }

    template<typename T>void operator()(T const&t) const { out<<t<<", "; }
};

cout << '[';
for_each( printer(cout), make_tuple(0,.1,"hello") );
cout << ']';
0 голосов
/ 21 сентября 2010

Это одно из самых простых решений, которое я могу придумать.Я успешно проверил это с GCC 4.4:

#include <iostream>
#include <tuple>

template<class T>
void push(T x)
{
  using namespace std;
  cout << x << '\n';
}

template<int Remaining>
struct push_tuple_helper
{
  template<class... Args>
  static void doit(std::tuple<Args...> const& t)
  {
    push(std::get<sizeof...(Args)-Remaining>(t));
    push_tuple_helper<Remaining-1>::doit(t);
  }
};

template<>
struct push_tuple_helper<0>
{
  template<class... Args>
  static void doit(std::tuple<Args...> const& t) {}
};

template<class... Args>
void push(std::tuple<Args...> t)
{
  push_tuple_helper<sizeof...(Args)>::doit(t);
}

int main()
{
  using namespace std;
  push( 42 );
  cout << "---\n";
  push( "Hello World" );
  cout << "---\n";
  push( make_tuple(42,3.14,"foo") );
}
0 голосов
/ 21 сентября 2010

Если вы хотите выполнить итерацию по кортежу с помощью функции, вы можете сделать это с помощью (небольшого количества) шаблона. Идея состоит в том, чтобы создать список целочисленных переменных, соответствующий индексам кортежей, а затем использовать std :: get для доступа к значениям. Быстро:

template<int...> struct indices;

// constructs an index list for a tuple e.g. indices<0, 1, 2, 3> 
template<class ... Args> some_index_type make_indices();

Затем вы можете расширить кортеж так:

template<class Args...> void foo(const std::tuple<Args...>& tup) {
    foo(tup, make_indices<Args...>());
}

template<class Args..., int...I> void foo(const std::tuple<Args...>& tup,
                                            indices<I...> ){
   bar( std::get<I>(tup)... );
}

Это расширит содержимое кортежа и подаст его на панель функций.

Надеюсь, это поможет:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...