[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