There's an class named PlotCurve. It describes a chart as a container of points and operations on them. A data for PlotCurve is gotten from the class RVDataProvider. Important thing is that the amount of points that is provided by RVDataProvider may be big (more than 1kk) so RVDataProvider returns a read-only pointer to Y data (X data can be calculated by index of the pointer) to improve the perfomance.
The main problem is that RVDataProvider has two different methods for two types:
class RVDataProvider : public QObject, public IRVImmutableProvider
{
public:
// ...
ReadonlyPointer<float> getSignalDataFloat(int signalIndex, quint64 start, quint64 count) override;
ReadonlyPointer<double> getSignalDataDouble(int signalIndex, quint64 start, quint64 count) override;
// ...
}
ReadonlyPointer<T> is only a read-only wrapper of a C-style pointer.
In order to get a curve's range of values (for looking for min-max, painting them on the canvas, etc) I am supposed to declare different functions too.
class PlotCurve : public QObject
{
public:
// ...`
virtual ReadonlyPointer<float> getFloatPointer(quint64 begin, quint64 length) const;
virtual ReadonlyPointer<double> getDoublePointer(quint64 begin, quint64 length) const;
// ...
}
It leads to using switch statement in the client code and its changes if the new available type of data is added.
switch (dataType())
{
case RVSignalInfo::DataType::Float: {
auto pointer = getFloatPointer(begin, length);
Q_ASSERT(!(pointer).isNull()); \
for (quint64 i = 0; i < (length); ++i) { \
auto y = (pointer)[i]; \
if (y < (minY)) { (minY) = y; continue; } \
if (y > (maxY)) { (maxY) = y; } \
}
} break;
case RVSignalInfo::DataType::Double: {
auto pointer = getDoublePointer(begin, length);
Q_ASSERT(!(pointer).isNull()); \
for (quint64 i = 0; i < (length); ++i) { \
auto y = (pointer)[i]; \
if (y < (minY)) { (minY) = y; continue; } \
if (y > (maxY)) { (maxY) = y; } \
}
} break;
// ...
}
Is there a way to get rid of dependencies to a client code? Three thing came to my mind:
1) Create Iterator type that would be a wrapper of ReadonlyPointer. Nope - performance is decreased to 10+ times because of iterator's virtual functions.
2) Create a traverse method that would be perform some function to every value in some range. Nope again - the most optimized version using function pointers is two times slower than switch statement in client code.
3) Make the class PlotCurve template. In this way I can't add different PlotCurves to the one container like it is now.