Применение функции к каждому элементу в пакете параметров происходит в неправильном порядке

Итак, у меня есть простая проблема, которую оказалось намного сложнее решить, чем я ожидал.

Код:

template <typename R, typename... Args>
void ApplyString(R(*func)(Args...), std::string args)
{
    auto argtuple = std::make_tuple(GetToken<Args>(args)...);
    typename GetIndexTuple<Args...>::type indices;
    ApplyTuple(func, argtuple, indices);
}

Цель:

Цель кода - токенизировать каждый аргумент функции из строки. Что я делаю, так это применяю функцию токенизации, поскольку я расширяю пакет параметров (Args...) до кортежа. Затем я использую эту функцию для вызова ApplyTuple, который делает то, что он говорит. И это действительно работает ... вроде как ...

Проблема:

Проблема в том, что в последовательности make_tuple последняя функция вызывается первой. Например, допустим, у меня есть простая функция вывода:

void Output(int a, int b, int c)
{
    std::cout << a << "," << b << "," << c << "\n";
}

Потом использую так:

ApplyString(Output, "10 7 5 ");

Ожидаемый результат: 10,7,5. Однако фактический результат: 5,7,10. Мне было интересно, знает ли кто-нибудь способ помочь мне обойти это. Насколько я понимаю, кажется, что порядок вызова функций противоположен порядку расширения пакета параметров. Я правильно понимаю?

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


person Toms Jensen    schedule 04.12.2013    source источник
comment
Используйте инициализацию списка, которая гарантирует порядок оценки слева направо, в отличие от аргументов функции, которые оцениваются в порядке, определенном реализацией.   -  person K-ballo    schedule 04.12.2013
comment
Вы имеете в виду std :: tuple ‹Args ...› argtuple = {GetToken ‹Args› (args) ...} ;? Потому что это вызывает у меня ошибку: ошибка C2440: «инициализация»: невозможно преобразовать из «списка инициализаторов» в «std :: tuple‹ int, int, int ›». Я не думаю, что это когда-либо сработает, поскольку список не сможет содержать данные разных типов.   -  person Toms Jensen    schedule 04.12.2013
comment
Да, именно список-инициализация. Просто потеряйте =, поскольку std::tuple конструкторы explicit. list может содержать разнородные типы, пусть вас не обманывают все однородные типы в std::.   -  person K-ballo    schedule 04.12.2013
comment
Я также пробовал инициализировать: std :: tuple ‹Args ...› argtuple {GetToken ‹Args› (args) ...} ;. Выход по-прежнему: 5,7,10. Когда вы отбрасываете '=', кажется, что это больше не 'список-инициализаторов', и поэтому теряется его создание слева направо.   -  person Toms Jensen    schedule 04.12.2013
comment
Затем я использую эту функцию для вызова ApplyTuple, который делает то, что он говорит Ошибка также может быть скрыта там или в генерации индекса (GetIndexTuple), или в GetToken.   -  person dyp    schedule 04.12.2013
comment
Генерация индексов работает нормально, тестировал тщательно. Apply tuple также делает то, что должен: применяет каждый элемент кортежа к тому, что вы ему даете. Я уверен, что проблема заключается в генерации кортежа из строки из-за решения, которое я разработал. Думаю, сейчас мне интересно, есть ли более элегантный способ сделать это.   -  person Toms Jensen    schedule 05.12.2013


Ответы (1)


Итак, я кое-что придумал, и, хотя это не самое приятное, но все же работает. Я создал функцию для создания кортежа указанного типа из строки, а затем просто использовал ее вместо make_tuple.

Код:

template <typename Head, typename... Tail>
std::tuple<Head, Tail...> StringToTuple(std::tuple<Head, Tail...>, std::string& args)
{
    auto head = std::make_tuple(GetToken<Head>(args));
    return std::tuple_cat(head, StringToTuple(std::tuple<Tail...>(), args));
}
std::tuple<> StringToTuple(std::tuple<>, std::string& args)
{
    return std::tuple<>();
}

template <typename R, typename... Args>
R ApplyString(std::function<R(Args...)> func, std::string args)
{
    std::tuple<Args...> argtuple = StringToTuple(std::tuple<Args...>(), args);
    return ApplyTuple(func, argtuple);
}

Теперь это работает, но если у кого-то есть лучшее решение, опубликуйте его. Ваше здоровье!

person Toms Jensen    schedule 04.12.2013
comment
зависит от того, что вы считаете лучшим ... вы можете разделить строку пробелами и вернуть токены в обратном порядке: p - person melak47; 04.12.2013
comment
Однако мой токенизатор довольно надежен, например, строка get token будет захватывать всю строку в кавычках или слово с разделителем-пробелом, в зависимости от того, что наступит раньше. Так что это действительно не сработает. - person Toms Jensen; 05.12.2013