2

In this page:

http://qwt.sourceforge.net/class_qwt_plot_curve.html#afd13c94e23520dacbc37b4d0fd036a8b

The method

void QwtPlotCurve::setRawSamples()

just saves the addresses of the data in the QwtPlotCurve, which is exactly what I want for efficiency.

While:

void QwtPlotCurve::setSamples()

uses QVector, which is more convenient. But it's only "explicitly shared". What does that mean? does save the pointer just like the first one?

I need to add a point to the plot each 50 ms. Deep-copying of data isn't the best solution!!! advice?

rubenvb
  • 74,642
  • 33
  • 187
  • 332
The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189

2 Answers2

7

It's juxtaposed against Qt's concept of "implicit sharing":

http://doc.qt.io/archives/qt-4.7/implicit-sharing.html

Even if you pass a QVector of data by value as a parameter in Qt, it will not copy the memory immediately. It will only make a copy if one of the vectors is changed.

I would have thought that the documentation saying "explicit sharing" in the setSamples case is just to draw attention to the fact that you're passing in QVectors by reference instead of by value:

void QwtPlotCurve::setSamples(
    const QVector< double > &xData,
    const QVector< double > &yData 
)

And I also would have thought they did this so that if you change the data in your vector (or free it), it will affect the data held onto by the plot curve. You'd not expect that if you thought the vectors were passed by value (you can't tell if you're just reading the callsite).

HOWEVER looking at the source code it appears that under the hood it's just making an implicitly-shared copy anyway. In qwt_plot_curve.cpp we have:

/*!
   \brief Initialize data with x- and y-arrays (explicitly shared)

   \param xData x data
   \param yData y data
   \sa QwtPointArrayData
*/
void QwtPlotCurve::setSamples( const QVector<double> &xData,
    const QVector<double> &yData )
{
    setData( new QwtPointArrayData( xData, yData ) );
}

We can see that QwtPointArrayData is declared in qwt_point_data.h like this:

class QWT_EXPORT QwtPointArrayData: public QwtSeriesData<QPointF>
{
public:
    QwtPointArrayData( const QVector<double> &x, const QVector<double> &y );
    QwtPointArrayData( const double *x, const double *y, size_t size );

    virtual QRectF boundingRect() const;

    virtual size_t size() const;
    virtual QPointF sample( size_t i ) const;
    const QVector<double> &xData() const;
    const QVector<double> &yData() const;

private:
    QVector<double> d_x;
    QVector<double> d_y;
};

The code for the constructor in qwt_point_data.cpp is just a simple assignment to d_x and d_y. Which goes back to plain ol' implicit sharing. So changes you make in the data you passed in will not be seen by the plot; you will pay for the copy being made at the time of such a modification.

If they were just going to do this, then why they bothered passing in a const reference (instead of just by value) is a mystery to me. The only "sharing" going on here seems to be implicit, so I don't know what the "explicitly shared" comment is supposed to mean.

Christophe Weis
  • 2,518
  • 4
  • 28
  • 32
  • So does that mean if I add a point to the arrays xData, yData, I'll see its effect on the plot immediately? that's exactly what I want!!!!!!!!! I'm planning to use something like FIFO to plot the points I add to those arrays. Is this what I'm looking for? – The Quantum Physicist Apr 21 '12 at 15:23
  • If you are making minor modifications to the vectors and want to avoid copies being made, you might try something like `myPlot->setSamples(QVector(), QVector());` *before* you make any changes to the vectors you passed in. That will get the plot to release the implicitly shared reference it is holding, so you won't have to pay for the copy that would happen on modification. Could help...[shrug] – HostileFork says dont trust SE Apr 21 '12 at 16:01
  • Thanks a lot. But wouldn't it be easier just to call setRawSamples() each time I add a point? – The Quantum Physicist Apr 21 '12 at 16:56
  • Well yes...but I was all focused on looking at the QVector methods. :) You can still perhaps use QVector and its `data()` with `setRawSamples` as long as you're careful about when you add the points and don't allow it to do a reallocation at a moment where Qwt might be looking at a stale pointer... – HostileFork says dont trust SE Apr 21 '12 at 17:30
  • I did it and it worked. However, I had to replot every 10 points (I had to down-sample by a factor of 10), which is acceptable :-)... I'm using the reference of QVector::front(), and I'm calling reserve at the beginning of the thingy so that I could avoid reallocation ;-), and each time I add a point, I update with the function setRawSamples(). So everything is perfect now!! Thanks for all the help! – The Quantum Physicist Apr 21 '12 at 17:36
3

Taken right from http://doc.qt.io/archives/qq/qq02-data-sharing-with-class.html

With explicit sharing, it is the user's responsibility, not the class's, to call detach() before modifying an object. If the user forgets to call detach(), all objects sharing the same data have their state modified, a very dangerous side-effect.

Explicitly shared classes are semantically similar to pointers. Compare the code on the left, which uses int *, with that on the right, which uses a fictitious explicitly shared Int class:

int *a = new int( 111 );    Int a( 111 );
int *b = a;                 Int b = a;
*b = 222;                   b = 222;
qDebug( "%d", *a );         qDebug( "%d", (int) a );

Both programs print 222. For the left-hand code this is what we would expect (the pointer syntax is a big hint), but for the right-hand code it comes as an unpleasant surprise. Explicit sharing may solve the ownership problem, but its misleading syntax discredits it as an alternative to pointers.

The Qt classes QMemArray, QImage, and QMovie owe their explicit sharing to history. To keep your head above water, choose one of the following guidelines when dealing with explicitly shared classes:

Avoid explicitly shared classes. Call detach() every time you're about to modify an object, unless you're certain that the object has no copy. This is highly error-prone. Call detach() every time you make a copy of an object: b = a; b.detach(); This effectively disables sharing, and means that you otherwise never need to call detach(). Use a copy() function if one is available:

b = a.copy();
Christophe Weis
  • 2,518
  • 4
  • 28
  • 32
chris
  • 60,560
  • 13
  • 143
  • 205
  • I'm pretty confused actually... I want to dynamically add points to my vector and avoid ALL TYPES of copying, and when I add a point I want to see its effect on the plot automatically!!! what would you advise in this case? – The Quantum Physicist Apr 21 '12 at 15:27
  • It basically means that it won't make a copy, it will take the actual variable. If you need to see it immediately, call whatever update function for the plot once you add the point. – chris Apr 21 '12 at 15:32
  • 1
    @chris Doesn't appear to be the case. See my answer for source-code-investigation...it may be an outdated comment that wasn't brought into sync (?) A conversation about this here: http://comments.gmane.org/gmane.comp.graphics.qwt.general/3228 – HostileFork says dont trust SE Apr 21 '12 at 15:46
  • @HostileFork Ah, that really helps things >.> – chris Apr 21 '12 at 15:50