I started to implement a very flexible Odometer. It may have several disks with even different amount of values on each different disk. And, as an extension, even the data type of values on each of the single disks could be different.
All this shall be implemented with one class. The number of template parameters defines the behavior of the class.
- 1 template parameter: Like
Odomoter<int>
shall result in an Odometer havingint
values on each disk. The resulting internal data type will be astd::vector<std::vector<int>>
- 2 or more template parameters: The number of template parameter will define the number of single disks of the Odometer. Each disk has the data type of the template parameter. In the case of
Odometer<char, int, double>
, this will result in a data typestd::tuple<std::vector<char>, std::vector<int>, std::vector<double>>
Now, I want to add a variadic constructor, where I can add whatever data. Of course types and number of arguments must match. I omit the check for the moment and will add it later.
So, now I have a templatized variadic class and a variadic constructor. So, I have the parameter pack off the class and the parameter pack of the constructor.
Now I would need iterate over the elements of both parameter packs at the same time in parallel.
Please see the below code example for an illustration of the problem (I deleted most of the code in the class, to just show you the problem):
#include <vector>
#include <tuple>
#include <list>
#include <initializer_list>
template<typename...Ts>
struct Odometer {
static constexpr bool IsTuple = ((std::tuple_size<std::tuple<Ts...>>::value) > 1);
template<typename...Ts>
using Tuples = std::tuple<std::vector<Ts>...>;
template<typename...Ts>
using MyType = std::tuple_element_t<0, std::tuple<Ts...>>;
template<typename...Ts>
using Vectors = std::vector<std::vector<MyType<Ts...>>>;
template<typename...Ts>
using Disks = std::conditional<IsTuple, Tuples<Ts...>, Vectors<Ts...>>::type;
Disks<Ts...> disks{};
template <typename...Args>
Odometer(Args...args) {
if constexpr (IsTuple) {
// Here disk is a std::tuple<std::vector<char>, std::vector<int>, std::vector<double>>
([&] {
//std::vector<MyType<Ts...>> disk{}; // Does not work. Or would always be a std::vector<char>
if constexpr (std::ranges::range<Args>) {
//for (const auto& r : args) // Does not work
//disk.push_back(r); // Does not work
}
else {
//disk.push_back(args); // Does not work
} } (), ...);
}
else {
([&] {
disks.push_back({});
if constexpr (std::ranges::range<Args>) {
for (const auto& r : args)
disks.back().push_back(r);
}
else {
disks.back().push_back(args);
} } (), ...);
}
}
};
int main() {
Odometer<char, int, double> odo2('a', std::vector{1,2,3}, std::list{4.4, 5.5});
}
I can iterate over the parameter pack of the constructor using a fold expression. I could also use std::apply
. But, I need to iterate also over the tuple elements of the "disks", defined by the class template parameters.
I do not want to use recursive templates.
So, I need to iterate of 2 parameter packs in parallel at the same time. How could this be done?
The only idea I have now is to use a helper class with a std::index_sequence
, but I do not know.
Please be reminded. Check of number of elements in parameter packs and type will be done later.