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.