Ну, маленькие биты включения (use_uv()
и т. Д. 1078 *) интересны, и я уверен, что не существует обобщенной c версии аналогичной функции, поэтому я дал ей go .
Чтобы сделать данные обобщенными c, вы должны заменить часть "struct" и имена этих прекрасных полей на индексы std::tuple
, структуры generi c. Вы можете восполнить это, расширив std::tuple
, добавив методы доступа
struct match_track_data_t : std::tuple<match_track_data_uints_t, delta_pair, std::string, std::string> {
using std::tuple<match_track_data_uints_t, delta_pair, std::string, std::string>::tuple;
match_track_data_uints_t& uv() { return std::get<0>(*this); }
match_track_data_uints_t& uv() const { return std::get<0>(*this); }
delta_pair& dv() { return std::get<1>(*this); }
delta_pair& dv() const { return std::get<1>(*this); }
/* etc... */
};
Для вектора, данные как кортеж векторов. Флаги use_*
становятся массивом предикатов:
template <typename... Ts>
class md_tracker_t {
std::tuple<std::vector<Ts>...> data;
std::array<bool(*)(), sizeof...(Ts)> use_ix;
public:
md_tracker_t(std::array<bool(*)(), sizeof...(Ts)> use_ix) : use_ix(use_ix) { }
Теперь я попытался классифицировать каждый метод в вашем классе как некое обобщенное c действие:
select
- что-то делает с первым активированным вектором и возвращает значение apply
- делает что-то с каждым активным вектором (или всеми векторами) apply_join
- архивирует кортеж с другим кортеж и что-то делает с каждой включенной парой get_mtd
- эту функцию я не смог преобразовать sh в другие типы
Функции apply*
должны иметь версия, которая применяет его только к включенным массивам или ко всем массивам.
Если вы сопоставите весь интерфейс publi c с этими обобщенными c функциями:
bool empty() const { return select([](auto& v) { return v.empty(); }); }
bool size() const { return select([](auto& v) { return v.size(); }); }
bool capacity() const { return select([](auto& v) { return v.capacity(); }); }
void clear() { apply<false>([](auto& v) { v.clear(); }); }
void shrink_to_fit() { apply([](auto& v) { v.shrink_to_fit(); }); }
void reserve( size_t const sz ) { return apply([&sz](auto& v) { v.reserve(sz); }); }
void resize ( size_t const sz ) { return apply([&sz](auto& v) { v.resize(sz); }); }
void swap( md_tracker_t &mt ) { apply_join<false>(mt, [](auto& v, auto& w) { v.swap(w); }); }
void push_back( std::tuple<Ts...> const &md ) { apply_join(md, [](auto& v, auto& e) { v.push_back(e); }); }
void copy_from( size_t const from_ix, size_t const to_ix ) { apply([&from_ix, &to_ix](auto& v) { v[to_ix] = v[from_ix]; }); }
void add_from( md_tracker_t const &mt, size_t const ix ) { apply_join(mt, [&ix](auto& v, auto& w) { v.push_back(w[ix]); }); }
std::tuple<Ts...> get_mtd( size_t const ix ) const { return get_mtd_impl(ix, std::index_sequence_for<Ts...>{}); }
Все, что вам нужно сделать это реализовать их! (Я пропускаю много стандартного здесь, используя C ++ 14 generi c lambdas).
Чтобы реализовать select
, вы не можете просто написать al oop, потому что std::tuple
может индексироваться только с аргументом времени компиляции . Так что вместо этого вам нужно go рекурсивно: начинать с нуля, и если [0] включено, применить лямбду к этому вектору ИЛИ повторить для следующего индекса:
template <typename Functor, size_t Index = 0, typename std::enable_if<Index != sizeof...(Ts)>::type* = nullptr>
decltype(auto) select(Functor&& functor) const {
return use_ix[Index]()
? functor(std::get<Index>(data))
: select<Functor, Index + 1>(std::forward<Functor>(functor));
}
template <typename Functor, size_t Index, typename std::enable_if<Index == sizeof...(Ts)>::type* = nullptr>
decltype(auto) select(Functor&& functor) const { return decltype(functor(std::get<0>(data))){}; }
apply
немного проще, потому что вы можете просто развернуть весь кортеж с помощью std::index_sequence
и std::get
в одноразовый логический массив. Оператор запятой (всегда возвращает правую часть) является своего рода чит-кодом, который превращает функцию void в выражение:
template <bool conditional = true, typename Functor, size_t... Is>
void apply(Functor&& functor, std::index_sequence<Is...>) {
std::initializer_list<bool> { (!conditional || use_ix[Is]() ? (functor(std::get<Is>(data)), false) : false)... };
}
template <bool conditional, typename Functor>
void apply(Functor&& functor) {
return apply(std::forward<Functor>(functor), std::index_sequence_for<Ts...>{});
}
Логический аргумент шаблона conditional
является в основном переопределением. Если false
, он будет применять лямбду независимо от того, что возвращает предикат.
Множество функций, таких как push_back
и swap
, принимают либо слайс, либо другой SoA и перекрестно соединяют его. Для них у нас есть apply_join
, что почти совпадает с apply
, за исключением того, что он обрабатывает дополнительный аргумент:
template <bool conditional = true, typename Functor, typename Arg, size_t... Is>
void apply_join(Functor&& functor, Arg& arg, std::index_sequence<Is...>) {
std::initializer_list<bool> { (!conditional || use_ix[Is]() ? (functor(std::get<Is>(data), std::get<Is>(arg)), false) : false)... };
}
template <bool conditional = true, typename Functor, typename Arg>
void apply_join(Functor&& functor, Arg& arg) {
return apply_join(std::forward<Functor>(functor), arg, std::index_sequence_for<Ts...>{});
}
Наконец, get_mtd
просто расширяет кортеж, применяет оператор индекса к каждому один, затем передает его std::tuple
:
template <size_t... Is>
std::tuple<Ts...> get_mtd_impl( size_t const ix, std::index_sequence<Is...>) const {
assert(ix < sizeof...(Ts));
return std::tuple<Ts...>(std::get<Is>(data)[ix]...);
}
И это все!
};
Возможно, больше кода, чем если бы вы только что сделали это вручную, но вы можете добавлять поля весь день без лишних шаблонов.
Использование:
using md_tracker = md_tracker_t<match_track_data_uints_t, delta_pair, std::string, std::string>;
Демонстрация : https://godbolt.org/z/p5t6wu