Я пытаюсь сохранить в std::tuple
различном количестве значений, которые позже будут использоваться в качестве аргументов для вызова указателя функции, который соответствует сохраненным типам.
Я создал упрощенный пример, показывающий проблему, которую я пытаюсь решить:
#include <iostream>
#include <tuple>
void f(int a, double b, void* c) {
std::cout << a << ":" << b << ":" << c << std::endl;
}
template <typename ...Args>
struct save_it_for_later {
std::tuple<Args...> params;
void (*func)(Args...);
void delayed_dispatch() {
// How can I "unpack" params to call func?
func(std::get<0>(params), std::get<1>(params), std::get<2>(params));
// But I *really* don't want to write 20 versions of dispatch so I'd rather
// write something like:
func(params...); // Not legal
}
};
int main() {
int a=666;
double b = -1.234;
void *c = NULL;
save_it_for_later<int,double,void*> saved = {
std::tuple<int,double,void*>(a,b,c), f};
saved.delayed_dispatch();
}
Обычно для задач, связанных с std::tuple
шаблонами или шаблонами с переменным числом переменных, я бы написал другой шаблон, например, template <typename Head, typename ...Tail>
для рекурсивной оценки всех типов один за другим, но я не вижу способа сделать это для отправки вызова функции.
Реальная мотивация для этого несколько сложнее, и в любом случае это в основном просто учебное упражнение. Вы можете предположить, что я передал кортеж по контракту из другого интерфейса, поэтому изменить его нельзя, но желание распаковать его в вызов функции мое. Это исключает использование std::bind
в качестве дешевого способа обойти основную проблему.
Каков чистый способ отправки вызова с использованием std::tuple
или альтернативный лучший способ достижения того же чистого результата хранения / пересылки некоторых значений и указателя функции до произвольной точки будущего?
auto saved = std::bind(f, a, b, c);
... а потом просто позвонитьsaved()
?Ответы:
Решение C ++ 17 заключается в простом использовании
std::apply
:Просто чувствуется, что об этом следует заявить один раз в ответе в этой теме (после того, как это уже появилось в одном из комментариев).
Базовое решение C ++ 14 все еще отсутствует в этой теме. РЕДАКТИРОВАТЬ: Нет, это на самом деле там в ответе Уолтера.
Эта функция дается:
Назовите его следующим фрагментом:
Пример:
DEMO
источник
http://coliru.stacked-crooked.com/a/8ea8bcc878efc3cb
std::make_unique
напрямую? Нужен ли конкретный экземпляр функции? 2. Почему,std::move(ts)...
если мы можем измениться[](auto... ts)
на[](auto&&... ts)
?std::make_unique
ожидаете кортеж, и кортеж может быть создан из распакованного кортежа только через другой вызовstd::make_tuple
. Это то, что я сделал в лямбда-выражении (хотя это очень избыточно, так как вы также можете просто скопировать кортеж в уникальный указатель без использованияcall
).Вам нужно собрать пакет параметров из чисел и распаковать их
источник
struct gens
общее определение (то, которое наследует от расширенного происхождения того же самого). Я вижу, что в конечном итоге она попадает в специализацию с 0. Если настроение вас устраивает и у вас есть запасные циклы, если вы можете расширить это и то, как оно используется для этого, я был бы вечно благодарен. И я бы хотел сто раз проголосовать за это. Мне было веселее играть с касательными из этого кода. Спасибо.seq<0, 1, .., N-1>
. Как это работает:gens<5>: gens<4, 4>: gens<3, 3, 4>: gens<2, 2, 3, 4> : gens<1, 1, 2, 3, 4> : gens<0, 0, 1, 2, 3, 4>
. Последний тип является специализированным, создающимseq<0, 1, 2, 3, 4>
. Довольно хитрый трюк.gens
на:template <int N, int... S> struct gens { typedef typename gens<N-1, N-1, S...>::type type; };
std::integer_sequence<T, N>
и ее специализацияstd::size_t
,std::index_sequence<N>
- плюс связанные с ними вспомогательные функцииstd::make_in(teger|dex)_sequence<>()
иstd::index_sequence_for<Ts...>()
. А в C ++ 17 есть много других хороших вещей, интегрированных в библиотеку - в частности, включаяstd::apply
иstd::make_from_tuple
, которые будут обрабатывать биты распаковки и вызоваЭто полная компилируемая версия решения Йоханнеса по вопросу awoodland, в надежде, что оно кому-нибудь пригодится. Это было протестировано с моментальным снимком g ++ 4.7 в Debian squeeze.
Можно использовать следующий файл SConstruct
На моей машине это дает
источник
Вот решение C ++ 14.
Это все еще нуждается в одной вспомогательной функции (
call_func
). Поскольку это распространенная идиома, возможно, стандарт должен поддерживать ее напрямую, как иstd::call
при возможной реализации.Тогда наша отложенная отправка становится
источник
std::call
. Хаотический зоопаркinteger_sequence
иindex_sequence
вспомогательные типы в C ++ 14 объясняются здесь: en.cppreference.com/w/cpp/utility/integer_sequence Обратите внимание на заметное отсутствиеstd::make_index_sequence(Args...)
, поэтому Уолтер был вынужден использовать более синтаксический синтаксисstd::index_sequence_for<Args...>{}
.Это немного сложно достичь (хотя это возможно). Я советую вам использовать библиотеку, где это уже реализовано, а именно Boost.Fusion ( функция invoke ). В качестве бонуса Boost Fusion работает и с компиляторами C ++ 03.
источник
C ++ 14решение. Во-первых, некоторые полезные шаблоны:
Они позволяют вам вызывать лямбду с рядом целых чисел времени компиляции.
и мы сделали.
index_upto
иindex_over
позволит вам работать с пакетами параметров без необходимости генерировать новые внешние перегрузки.Конечно, в C ++ 17 ты только
Теперь, если нам это нравится, в C ++ 14 мы можем написать:
сравнительно легко и получить уборщик C ++ 17 Синтаксис готов к отправке.
просто замените
notstd
наstd
когда ваш компилятор обновится, а bob станет вашим дядей.источник
std::apply
<- музыка для моих ушейindex_upto
и менее гибок. ;) Попробуйте позвонитьfunc
с аргументами назад сindex_upto
иstd::apply
соответственно. По общему признанию, кто, черт возьми, хочет вызвать функцию из кортежа назад.std::tuple_size_v
это C ++ 17, поэтому для решения C ++ 14 его нужно заменить наtypename std::tuple_size<foo>::value
value
это не тип. Но исправлено в любом случае.sizeof...(Types)
. Мне нравится ваше решение безtypename
.Размышляя о проблеме, я еще нашел ответ на этот вопрос. Я нашел другой способ решения этой проблемы:
Что требует изменения реализации
delayed_dispatch()
:Это работает путем рекурсивного преобразования
std::tuple
в пакет параметров самостоятельно.call_or_recurse
необходима как специализация для завершения рекурсии реальным вызовом, который просто распаковывает завершенный пакет параметров.Я не уверен, что это в любом случае «лучшее» решение, но это другой способ думать и решать его.
В качестве другого альтернативного решения, которое вы можете использовать
enable_if
, чтобы сформировать что-то, возможно, более простое, чем мое предыдущее решение:Первая перегрузка просто берет еще один аргумент из кортежа и помещает его в пакет параметров. Вторая перегрузка принимает соответствующий пакет параметров, а затем выполняет реальный вызов, при этом первая перегрузка отключается в одном-единственном случае, когда вторая будет жизнеспособной.
источник
Мой вариант решения от Йоханнеса с использованием C ++ 14 std :: index_sequence (и возвращаемый тип функции в качестве параметра шаблона RetT):
источник