KProcess Line Mode Patch

Thiago Macieira thiago at kde.org
Mon Dec 1 14:26:14 GMT 2008


On Monday 01 December 2008 12:32:08 Ralf Habacker wrote:
> Hi,
>
> based on the work described on
> http://lists.kde.org/?l=kde-core-devel&m=120522889930459&w=2 there is a
> patch appended which implements a line buffering mode directly in KProcess.
> The advantage of this approach is that this feature is directly
> available from kdecore and therefore no additional class located
> somewhere in the related package source is required.
>
>  How it works:
>
> 1.Line buffering mode ist enabled with the
>
>     void setOutputLineMode(bool mode);
>
> 2.There are new KProcess signals available which indicates available lines.
>
>     void readyReadLineStandardOutput();
>     void readyReadLineStandardError();
>
> 3. The lines could be fetched with
>
>     QStringList &readLinesStandardOutput(QStringList &out=QStringList());
>     QStringList &readLinesStandardError(QStringList &out=QStringList());
>
> 4. non standard line delimiters could be set by
>
>     void setOutputLineDelimiter(QString &delim=QString());
>
> Any comments or objections ?

Yes, I object :-)

A few problems with your approach:

1) you're using QByteArray for storing the buffer. That's incredibly 
inefficient for large buffers. Imagine that the child program is writing 1MB 
lines: that will cause a lot of memory reallocations and memcpy.

Please use peek() from QIODevice to determine if there's a newline in the 
buffer. You could/should also restrict the length of the search for newline. 
By doing this, you let QIODevice / QProcess manage the buffer, which it does 
using the more-efficient QRingBuffer class (not public)

If you choose not to do this, then I recommend you only read the first line. 
There's no need to keep reading in _k_receivedStdout and _k_receivedStderr if 
you already have one line read.

2) Note that, with your patch, then you *cannot* use the normal 
QProcess/KProcess functions read(), readLine(), peek(), etc. because you 
drained the buffers. KProcess does not override the QProcess virtuals 
readData() and writeData(), meaning those still operate on QProcess buffers.

In fact, with your patch, you're restricted to the new line mode. The non-line 
mode will not work.

3) The API you presented:

    QStringList &readLinesStandardOutput(QStringList &out=QStringList());
    QStringList &readLinesStandardError(QStringList &out=QStringList());

is IMHO awful. It has no parallel in Qt or KDE core classes. It violates the 
Library Coding Policy by returning a reference. And it takes a reference as a 
parameter, which has a default value(!). Worse yet, you return that temporary 
as a non-const reference. 

I think the compiler doesn't even let you write:
	QStringList &out = QStringList();

See:
$ echo 'class Foo {}; int main() { Foo &foo = Foo(); }' | \
pipe> /usr/bin/g++ -fsyntax-only -xc++ -
<stdin>: In function ‘int main()’:
<stdin>:1: error: invalid initialization of non-const reference of type ‘Foo&’ 
from a temporary of type ‘Foo’

Please use QString returns and read one line at a time. That will parallel the 
readLine() function that is already there, by extending it to match what the 
readAll()/readAllStandardError()/readAllStandardOutput() do. That would 
require you to have a canReadLineStandardError and canReadLineStandardOutput() 
already.

4) Your signals emit only once. The readyRead(), readyReadStandardOutput() and  
and readyReadStandardError() signals emit whenever there's new data. 

If you choose to continue with this, your code should emit the signals 
whenever there's a new newline. But I don't see a way of efficiently 
implementing this without direct access to QProcess's buffers -- which 
QProcess won't give you.

5) Finally, I'm not convinced of the need for this. Why can't people call 
canReadLine() when readyRead() is emitted?

What I'd recommend instead is a line-based reader that works on top of a plain 
QIODevice. That way, you can plug it on a QTcpSocket, QLocalSocket, 
QNetworkReply or KTcpSocket objects as well. (the finished() signal is 
replaced by the readChannelFinished() signal)

This would have the issue, of course, that it doesn't handle a multi-channel 
device like QProcess.

-- 
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
  Senior Software Engineer - Nokia, Qt Software
  Qt Software is hiring - ask me
      PGP/GPG: 0x6EF45358; fingerprint:
      E067 918B B660 DBD1 105C  966C 33F5 F005 6EF4 5358
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part.
URL: <http://mail.kde.org/pipermail/kde-core-devel/attachments/20081201/56e5dcf1/attachment.sig>


More information about the kde-core-devel mailing list