[Development] API for multiple (but variable number of) return arguments
Andre Somers
andre at familiesomers.nl
Sat Jul 27 11:36:56 CEST 2013
Hi,
It has been quite awhile, but I'd like to finally pick up QTimeSpan
again and this time really get it into Qt. The idea of this class is
that it represents a duration of time, optionally anchored to a point in
time, and can output and parse this data to and from strings in
different formats, including using 'natural' units like years or months
for long time spans. One of the thing I'd like to do is shrink and
simplify the API before submitting it.
It is not the idea of QTimeSpan in itself that I'd like to discuss, but
one API issue I'm currently looking at.
For the sake of discussion, let's assume that we have the following already:
namespace Qt {
enum TimeSpanUnit {
Years = 0x01,
Months = 0x02,
Weeks = 0x04,
Days = 0x08,
Hours = 0x10,
Minutes = 0x20,
Seconds = 0x40,
MilliSeconds = 0x80
};
Q_DECLARE_FLAGS(TimeSpanFormat, TimeSpanUnit);
};
In order to get a time span in the units that you'd like to use, you
need a function that can somehow return the values for all these units
at the same time, because in order to calculate the number of days, you
also need to know if the user also requested the number of weeks
(compare "23 days" with "3 weeks, 2 days"). Ideally, there would be a
return value as well to indicate success or failure, because time spans
without an anchor date cannot be expressed as months or years.
There are several ways to write a (member) function to do this. This is
what I came up with so far:
1) pointers to ints
bool getUnits(int* years, int* monts = 0, int* weeks = 0, int* days = 0,
int* hours = 0, int* minutes = 0, int* seconds = 0, int* milliSeconds = 0);
Every argument may be 0, in which case this unit is considered as
not-used. This works (I have this in the current version of QTimeSpan),
but it is not a very nice API to work with. If you're interested in
minutes and seconds, you end up with a call like this:
int minutes, seconds;
if (getUnits(0,0,0,0,0, &minutes, &seconds))
{ ...}
2) returning a map
QMap<Qt::TimeSpanUnit, int> getUnits(Qt::TimeSpanFormat);
In this case, the units to use are encoded in a single flag, and the
result is a single data structure. I guess the easiest way to return
failure is to return an empty map. The equivalent call from the first
option would look like:
int minutes, seconds;
QMap<Qt::TimeSpanUnit, int> result = getUnits(Qt::Minutes | Qt::Seconds);
if (!result.isEmpty()) {
minutes = result.value(Qt::Minutes);
seconds = result.value(Qt::Seconds);
};
In my opinion, not all that great either, but better than 1). It may be
a benefit to have the results in a data structure that's easy to pass
along in one go, but if you don't want that, getting them out is a bit
of a hassle perhaps.
3) QString::arg like API
An interesting option is to do something like this:
int minutes, seconds;
if ( getUnits().unit(Qt::Minutes, minutes).unit(Qt::Seconds, seconds) ) {
//blah
};
Downside is that it allows specifying the same unit multiple times. That
does not need to be a real problem (simply return the same value more
than once), but it may look weird. Also, this API is implementation wise
quite a bit more complex than the previous options. The resulting code
is more verbose than option 1, but much easier to read.
4) separate requests
Another option is not return all relevant units in one go, but to use
multiple function calls:
int getUnit(Qt::TimeSpanFormat format, Qt::TimeSpanUnit unit);
Failure would return -1. The example would yield:
Qt::TimeSpanFormat format = Qt::Minutes | Qt::Seconds;
int minutes = getUnit(format, Qt::Minutes);
int seconds = getUnit(format, Qt::Seconds);
if (minutes >= 0 && seconds >=0) {
//blah
}
Downside is that in order to make this efficient, you'd need to cache
the result, otherwise you have to reconstruct it for every call with the
same format. Personally, I don't like having to make multiple function
calls to retrieve parts of the same answer. Also, there are
opportunities for errors here, such as asking for units that are not in
the format.
I guess there are more options, but these four are the more serious
candidates I could come up with. Did I overlook a good candidate? Where
are other examples in the Qt API where there were similar needs, so I
can look if their patterns may apply here?
Anyway, I am looking for feedback. Say that this will be included in Qt
some day, what is the most Qt-like API in your opinion?
André
More information about the Development
mailing list