Friday 27 August 2010

Another day, another itch.

(First, an apology in advance, this is a bit of a braindump, and as such, isn't very well structured.)

I've recently been diving into SQL support in Qt, provided by the QtSql module. Specifically, I've been looking into providing IPC on top of QSqlTableModel.

(For those of you who aren't too technically oriented, I'm trying to have multiple processes have the same basedata store, and know when another process modifies the data in some way, to keep all processes in sync).

While I won't go into details on that now, as it isn't complete, it has been an interesting journey, and I think I've found yet another few things I want to add to my todo list. You see, QSqlTableModel is a great class, but it has a number of shortcomings.

It can't be used in a tree

This one is pretty obvious. It is, after all, a table - not a tree. However, making it represent a tree wouldn't be all that difficult I think (a customised implementation of QAbstractItemModel::parent() method looking at e.g. the 'parent' column of a table and returning a QModelIndex to the appropriate row or similar).

I don't believe that this can be solved in the current implementation, though, as QSqlTableModel inherits QSqlQueryModel which inherits QAbstractTableModel - meaning there's a non-trivial amount of tableness about the current implementation.

For some data (think a task manager: you might have subtasks under a task), this is an annoying limitation.

Incremental fetching isn't always supported

I'm actually not positive on this point, but it looks like incremental fetching is left up to the database driver, meaning that on some database types, you won't have incremental fetching.

On a large database containing thousands of rows (or gigabytes of data) this could be a very bad thing if I am correct.

Partial updates aren't really supported

If you delete a row from a database table, what would you expect the client to do after deleting that row?

I know I certainly wouldn't expect it to re-run the query to refetch the data, yet that's exactly what QSqlTableModel seems to do. Not nice.

This also happens on any other conditions of alteration to the data, which strikes me as being rather inefficient.

Furthermore, it isn't really possible to update values on the fly; this proved to be quite an annoyance when implementing IPC notifications of updates. QSqlQueryModel internally contains a QSqlQuery. QSqlQuery has a ::value() method, to retrieve a QVariant containing the data in a given row of a result set. It doesn't, however, provide any method to change that data (even in memory), meaning that once again, a full reload is the only real solution to something else changing the data out from under you.

This isn't really so unexpected, as it's a bit of an odd use-case, but it has made my implementation trickier than I'd wish.

Conclusion?

I wish. I don't know what to do, yet, but I suspect it's going to involve writing code.

Thoughts welcome.

Labels: , , , , ,

Sunday 8 August 2010

Strings and Qt

One thing which comes up quite often when I'm talking to developers new to Qt is the topic of strings, more specifically, character encoding: how to do it right, what options are available, and best practices.

Having written this a few times now, I thought that perhaps it was about time I write it up in a more permanent location (here) in the hopes that people will stumble across it and magically become enlightened, and end world hunger. ;)

Qt (and C++) have a number of different string types.

QString

Qt has a string type in QtCore called QString. QString, internally, stores data in utf16, and *does* have knowledge of character encoding.

Services across a network (like web services) often want data in utf8. QString, however, stores data in utf16. To get to utf8, you want QString::toUtf8(). To convert from utf8 back to utf16 QString (e.g. parsing input from a web service) see, QString::fromUtf8().

std::string

C++ also has std::string (although you won't find a lot of this in Qt applications). Simply put, it's a wrapper around a C string providing convenience operations and nicer syntax. It still doesn't have such (fairly essential) things like character encoding.

You probably want to avoid using this in an internationalized application or one requiring interaction with network services unless you find your own solution for encoding issues.

QString has ::toStdString() and ::fromStdString() methods if you must use them for whatever reason.

C strings (char*)

Finally, you have C strings (char*) which don't have any idea what encoding is, they are just a bunch of bytes.

Generally speaking, they're latin1 encoded (ASCII), to put them into a QString.. QString::fromLatin1(). If they aren't latin1, see QTextCodec::setCodecForCStrings().

QLatin1String class is also helpful - in particular, this will allow you to compile when using QT_NO_CAST_FROM_ASCII (which itself is helpful to make sure you explicitly give encodings for all of your strings).

Labels: , , , , ,

achieving openness: it's about the journey, not the destination

I have written a lot of articles previously about project openness, and I've had this one cooking in drafts for a while without time to write much around the actual issues I'm presenting.

With some more thought, I realised that the best way to proceed is just to publish, and not point fingers and draw conclusions (though I certainly did write this with some projects in mind), so here goes.
  • Do you reject contributions for non-technical reasons?
  • Do you have development documentation (e.g. build instructions, architecture information) available for external contributors?
  • Do you answer questions on design, architecture, etc. from external contributors?
  • Do you work with contributors to polish their contributions and educate them as to best practices and your project?
  • Do you let external contributors take part in design decisions?
  • Do you hold external contributions to the same standards of review as internal contributions?
  • Do you grant rights (such as commit access, ability to close bugs) to external contributors?
  • Do you have a public means for (preferably real time) communication that you use?
  • Do you have a public issue tracker?
  • Do you use your public infrastructure wherever possible unless an issue is explicitly private?
Run your project against the list -- perhaps you'll find some things you can improve on.

If you've any suggestions to add to the list, why not write a comment? :)

Labels: , , , ,