[Development] API for multiple (but variable number of) return arguments

Simon Hausmann simon.hausmann at digia.com
Tue Jul 30 11:49:35 CEST 2013


On Saturday 27. July 2013 11.36.56 Andre Somers wrote:
> 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?

My favourite among those options is basically number 4. A property based API 
with as many getters are needed. It creates readable code, the implementation 
can take care of making things fast (caching) and it maps perfectly to 
JavaScript as well.

There is also the option of an API similar to QURL where the getter takes a 
parameter that describes how the returned value should be formatted, but that 
unfortunately doesn't really map to property "intense" languages like QML/JS.



Simon



More information about the Development mailing list