I am attempting to figure out how to achieve runtime polymorphism with a class called Signal
, where its data type is passed as enum value to the constructor. Compile-time polymorphism with templates is not suitable.
As all values in the "signal" are supposed to be of same type, I chose variant-of-vectors instead of vector-of-variants.
enum class Type { UINT8, INT16, FLOAT };
using VariantVector = std::variant<std::vector<uint8_t>, std::vector<int16_t>, std::vector<float>>;
class Signal {
public:
explicit Signal(size_t size, Type type, double value)
: m_size(size), m_type(type) {
// Create variant vector of said type and initialize it with value
}
// Various methods ...
private:
size_t m_size;
Type m_type;
VariantVector m_data; // Variant of vectors
};
I have been able to make most of the things work, but got stuck on how to override the iterator methods. In order to use range-based loops as
Signal signal(100, Type::UINT8, 10);
for(auto& item : signal){
// Do stuff with item
}
the begin()
and end()
functions need to be defined. I managed to partially solve this with explicit template functions as
template <typename T>
std::vector<T>::iterator begin();
template <typename T>
std::vector<T>::const_iterator begin() const;
template <typename T>
std::vector<T>::iterator end();
template <typename T>
std::vector<T>::const_iterator end() const;
but this does not work for range-based loops (and I'd like to avoid explicit templates anyway).
I tried to implement a couple solutions with std::visit
, but did not get far - most solutions were based on operator overloading. Also looked into the double-dispatch visitor pattern, but don't really see how to apply it here.
How to achieve this with a variant of vectors?
Is this even possible or am I attempting to bend some rules of C++ wizardry?
Any insight is more than welcome. Thanks.
EDIT #1:
@Wyck asked in the comments how I'd like range-based loop to work. Ideally I'd like a situation as:
{
Signal signal(100, Type::UINT8, 10);
for(auto& item : signal){
// item is of type uint8&
}
}
{
Signal signal(100, Type::FLOAT, 10);
for(auto& item : signal){
// item is of type float&
}
}
Note the reference to the type, which means changing it should modify the private variant contents of Signal as well.