[Development] API for multiple (but variable number of) return arguments
Philip Ashmore
contact at philipashmore.com
Sat Jul 27 14:05:12 CEST 2013
Hi there.
On 27/07/13 10:36, 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);
You could wrap this into a struct:
struct TimeSpanResult {
int m_years, m_months, m_weeks, m_days, m_hours, m_minutes,
m_seconds, m_milliseconds;
};
and return it from the call:
TimeSpanResult getUnitsTimeSpan(Qt:Minutes | Qt:Seconds);
I just used a different name as the return type can't be overloaded.
TimeSpanResult getUnitsTimeSpan(int flags)
{
TimeSpanResult res;
getUnits((flags & Qt:Years) ? & res.m_years : (int *)0, (flags &
Qt:Years) ? & res.m_months : (int *)0, ...
return res;
}
I've left out error checking for clarity.
>
>
> 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é
>
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development
Regards,
Philip Ashmore
More information about the Development
mailing list