Given a class that is defined to operate on a collection of complex types defined in a parameter pack, I've come up with this pattern to index the types in the parameter pack at runtime:
#include <iostream>
#include <variant>
template <typename T>
std::pair<typename T::typeK, typename T::typeV> pushdata_deserialize_impl(const uint8_t *serialized);
template <typename K, typename V>
class DataClass{
public:
using typeK = K;
using typeV = V;
};
template <class... DataClassCollection>
class DeSerializer{
public:
using VariantType = std::variant<std::monostate, std::pair<typename DataClassCollection::typeK, typename DataClassCollection::typeV>...>;
explicit DeSerializer(){}
VariantType pushdata(const uint8_t *dat_serialized, uint8_t datatype_id){
// deserialize based on the datatype_id, receive a variant of pairs
VariantType dat = pushdata_deserialize_helper(static_cast<int>(datatype_id), dat_serialized);
return dat;
}
// this returns a variant with monostate in it
auto pushdata_deserialize_helper(int index, const uint8_t *serialized) {
// returns a tuple
VariantType result;
size_t count = 0;
((index == count++ ? result = pushdata_deserialize_impl<DataClassCollection>(serialized) : std::monostate()), ...);
return result;
}
};
template <>
std::pair<int, int> pushdata_deserialize_impl<DataClass<int, int>>(const uint8_t *serialized) {
std::cout<<"Called deserialize for <int, int>"<<std::endl;
return std::make_pair<int, int>(1, 1);
}
template <>
std::pair<int, float> pushdata_deserialize_impl<DataClass<int, float>>(const uint8_t *serialized) {
std::cout<<"Called deserialize for <int, float>"<<std::endl;
return std::make_pair<int, float>(2, 2.0);
}
int main() {
// some input array to be processed
uint8_t serialized[3] = {0,1,2};
// works because no repetition of feeder types
DeSerializer<DataClass<int, int>, DataClass<int, float>> service_pushdata_working;
service_pushdata_working.pushdata(serialized, 2);
// does not work because there is a repetition of DataClass<int, float>
DeSerializer<DataClass<int, int>, DataClass<int, float>, DataClass<int, float>> service_pushdata_bugged;
service_pushdata_bugged.pushdata(serialized, 2);
return 0;
}
The important is the line
((index == count++ ? result = pushdata_deserialize_impl<DataClassCollection>(serialized) : std::monostate()), ...);
which iterates the types in the pack and if the index matches the count, the correct implementation of pushdata_deserialize_impl is called. This works fine as long as the types in the parameter pack are unique.
However, this pattern breaks down as soon as I have duplicate types in my parameter pack, like so:
DeSerializer<DataClass<int, int>, DataClass<int, float>, DataClass<int, float>>
How can I solve this? I cannot modify the DataClass type as it is given by another codebase.
Note that while the repeating type in the parameter pack may seem useless in this context, the actual implementation will trigger different logic for each type in the parameter pack, even if the types may be repeating.