From RxBinding<T>::Ptr
T
не может быть выведено, так как это невыведенный контекст из-за вложенных типов (см. Пример 1) в невыведенных контекстах на cppreference и godbolt example ), поэтому исходный пример не должен работать с выводом аргументов. Имея это в виду, наличие typename RxBinding<Ts>::Ptr ...args
будет работать так же, как и раньше (обратите внимание на синтаксис, имеющий ...
перед именем аргумента). Я изменил шаблон типа Variadi c на Ts
вместо T
, чтобы лучше представить, что это вариант c.
С auto binding = makeValueBinding(std::make_tuple( /* what do do here with args ?? */ ));
вы можете использовать расширение pack с шаблоном args->Get()
, поэтому последняя строка будет
auto binding = makeValueBinding(std::make_tuple(args->Get()...));
.
Создание переменных s0
, s1
и т. д. не является тривиальным делом, поэтому я вернитесь к нему в конце.
Чтобы сделать окончательную подписку, вам нужно будет использовать вспомогательную функцию для расширения кортежа:
template<typename ...ArgTypes, typename ...Ts, std::size_t ...Ns>
void FinalSubscribeHelper(
std::tuple<ArgTypes...> const &args,
std::tuple<Ts...> const &t,
std::index_sequence<Ns...>
)
{
// using C++17's fold expressions (https://en.cppreference.com/w/cpp/language/fold)
((std::get<Ns>(args)->Update(std::get<Ns>(t))), ...); // we use the comma operator for expansion
return;
// using array initializers for C++11
using ArrayT = int[sizeof...(ArgTypes)];
ArrayT{
((
std::get<Ns>(args)->Update(std::get<Ns>(t)) // this is the pattern
), 0)...
};
return;
}
Таким образом, окончательная подписка будет
auto sN = binding->Subscribe([=](std::tuple<Ts...> const &t){
// Update the N bindings.
FinalSubscribeHelper(std::make_tuple(args...), t, std::make_index_sequence<sizeof...(Ts)>{});
});
Для добавления всех подписок в очистку вам понадобится еще одна вспомогательная функция:
template<typename BindingT, typename ...STs, typename SNT, std::size_t ...Ns>
void CleanupHelper(
BindingT const &binding,
std::tuple<Ts...> const &s,
SNT const &sN
std::index_sequence<Ns...>
)
{
// using C++17's fold expressions (https://en.cppreference.com/w/cpp/language/fold)
(binding->CleanupWith << ... << std::get<Ns>(s)) << sN;
return;
// using array initializers for C++11
/*
this only works if
binding->CleanupWith << s0 << s1 << sN;
is equivalent to
binding->CleanupWith << s0;
binding->CleanupWith << s1;
binding->CleanupWith << sN;
*/
using ArrayT = int[sizeof...(ArgTypes)];
ArrayT{
((
binding->CleanupWith << std::get<Ns>(s)
), 0)...
};
binding->CleanupWith << sN;
return;
}
Итак, окончательная очистка будет CleanupHelper(binding, s, sN, std::make_index_sequence<sizeof...(Ts)>{});
.
Теперь вернемся к созданию s
. Чтобы создать обратный вызов, я предполагаю, что вы хотите, чтобы Update
назывался
b->Update(std::make_tuple(/* bM->Get() with M = 0, 1, 2, ..., I-1 */, vI, /* bM->Get() with M = I+1, I+2, ..., N-1 */));
. Для этого вам понадобятся две последовательности индексов: от 0
до I-1
и от I+1
до N-1
. Для этого давайте создадим несколько псевдонимов типов, чтобы сделать необходимые std::index_sequence
.
template<std::size_t Offset, typename T>
struct AddOffset;
template<std::size_t Offset, std::size_t ...Ns>
struct AddOffset<Offset, std::index_sequence<Ns...>>
{
using type = std::index_sequence<(Ns + Offset)...>;
};
template<std::size_t Offset, typename T>
using AddOffsetT = typename AddOffset<Offset, T>::type;
// this creates a std::index_sequence with the values
// Start, Start+1, Start+2, ..., End-1
template<std::size_t Start, std::size_t End>
using MakeIndexSequenceInRange = AddOffsetT<Start, std::make_index_sequence<End - Start>>;
Для создания s
вам понадобятся несколько вспомогательных функций:
template<typename BindingT, typename ...ArgTypes, typename VT, std::size_t ...Ns, std::size_t ...Ms>
void SubscribeCallbackHelper(
BindingT const &b,
std::tuple<ArgTypes...> const &args,
VT const &v,
std::index_sequence<Ns...>,
std::index_sequence<Ms...>
)
{
b->Update(std::make_tuple(std::get<Ns>(args)->Get()..., v, std::get<Ms>(args)->Get()...));
}
template<typename BindingWeakT, typename ...ArgTypes, std::size_t ...Ns>
auto CreateS(
BindingWeakT const &bindingWeak,
std::tuple<ArgTypes...> const &args,
std::index_sequence<Ns...>
) -> decltype(std::make_tuple(std::get<Ns>(args)->Subscribe(std::declval<void(*)(ArgTypes const &)>())...))
// I'm not sure this decltype will work, if you have C++14 you should be able to just use auto as a return type
{
return std::make_tuple(
std::get<Ns>(args)->Subscribe([bindingWeak, args](ArgTypes const &v) {
auto b = bindingWeak.lock();
if (b)
SubscribeCallbackHelper(b, args, v, MakeIndexSequenceInRange<0, Ns>{}, MakeIndexSequenceInRange<Ns+1, sizeof...(ArgTypes)>{});
})
);
}
Итак, создание s
будет
auto s = CreateS(bindingWeak, std::make_tuple(args...), std::make_index_sequence<sizeof...(Ts)>{});