An elaborate joke?

I started writing Grantlee::Tubes some time in December 2010. In the course of writing it I’ve mostly been researching what the dependable API of QIODevice is. I don’t know when Tubes will get into a release of the Grantlee libraries, but it probably won’t be the next release. All of the classes don’t do error handling that they could do yet, and the QIODevice research is still ongoing.

Nevertheless I decided to start publishing blogs about Tubes in mid-March to give context to the April fools post about it.

Talk about building up a joke.

The library and concepts are real and useful though, so I’ll push on with publishing these experimental devices to the repo.

Consider a use case where you want to read from a QIODevice that is being written to by another class. For example, QTextDocumentWriter writes stream of valid ODF XML to a QIODevice and QXMLStreamReader reads a stream of XML data from a QIODevice.

How can we connect them together?

One way might be to use a QBuffer.

  QBuffer buffer;
  QTextDocumentWriter writer(&buffer);
  writer.write(document);

  buffer.seek(0);

  QXMLStreamReader reader(buffer);

  // ... Do something with the XML.

This works, but it’s not a generic solution. If we wanted to do asynchronous writing of data to the device and asynchronous line based reading from it, we would have to make the buffer a member of a class, and when reading from it we would have to do something like this:

  void MyClass::onReadyRead()
  {
    if (!m_buffer->canReadLine())
      return;
    m_buffer->seek(0);
    const QByteArray line = m_buffer->readLine();
    m_buffer->buffer.chop(line.length());
    m_buffer->seek(m_buffer->size());
    useLine(line);
  }

Reading from a buffer does not invalidate the data it holds. We have to use a method returning the QByteArray to chop off the part we read ourselves. We also have to remember to seek() a few times on the buffer. I didn’t even try the code out for off-by-ones.

Enter Grantlee::Channel.

Grantlee::Channel already made an appearance in my last post. The idea is to solve a connection problem with QIODevices. While Pump can transfer data from a device that should be read to a device that should be written to, Grantlee::Channel is an intermediary providing both a device that consumes data and one that produces data.

The difference between Grantlee::Pump and Grantlee::Channel

There are several significant differences between it and QBuffer. QBuffer is not a sequential device, but Channel is. That means that The pos() and seek() methods are relevant API when working with a QBuffer, but irrelevant and meaningless when working with a Channel. As an implementor of the QIODevice API that means I don’t have to implement any meaning into those virtual methods and can ignore them. Instead I can implement the readData and writeData methods to implement a FIFO semantic. The Channel can be written to at any time, and read from whenever it has data. There is no need for seek()ing, and it automatically discards data that has been read, meaning no manual memory conservation responsibility for the caller.

    QTextDocument *document = getDocument();

    Grantlee::Channel *channel = new Grantlee::Channel(this);
    channel->open(QIODevice::ReadWrite);

    // Write to the channel
    QTextDocumentWriter *writer = new QTextDocumentWriter(channel, this);

    // Read from the channel
    QXmlStreamReader reader(channel);

    // Use the reader.

Easy.

2 Responses to “An elaborate joke?”

  1. Marc Says:

    > Easy.

    Unless you want to use it to communicate between threads. QIODevice, as a QObject, cannot live in two threads simultaneously😦

  2. Kuba Ober Says:

    Of course if you would add some signals and slots to a custom `QIODevice`-derived class, you could have it usable for cross-thread communication. Simply connect a custom `Q_SIGNAL void dataAvailable(const QByteArray &)` to a slot of same signature and you’re done: the sender and the receiver can be in whatever thread you want to. This wouldn’t be a top performer, but for many uses it’d be more than enough. Ideally you’d want to have a thread-safe multiple-reader, single-writer FIFO. That’s what’s missing from Qt.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: