All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Cc: "Peter Maydell" <peter.maydell@linaro.org>,
	"Philippe Mathieu-Daudé" <philmd@redhat.com>,
	"QEMU Developers" <qemu-devel@nongnu.org>
Subject: Re: Questionable aspects of QEMU Error's design
Date: Fri, 03 Apr 2020 09:48:17 +0200	[thread overview]
Message-ID: <87imihrovy.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <cf346d30-c233-50a1-b1fa-f5be20bfd891@virtuozzo.com> (Vladimir Sementsov-Ogievskiy's message of "Thu, 2 Apr 2020 20:17:26 +0300")

Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:

> 02.04.2020 18:06, Markus Armbruster wrote:
>> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
>>
>>> 02.04.2020 11:55, Markus Armbruster wrote:
>>>> Peter Maydell <peter.maydell@linaro.org> writes:
>>>>
>>>>> On Thu, 2 Apr 2020 at 07:11, Vladimir Sementsov-Ogievskiy
>>>>> <vsementsov@virtuozzo.com> wrote:
>>>>>> Somehow, in general, especially with long function names and long parameter lists I prefer
>>>>>>
>>>>>> ret = func(..);
>>>>>> if (ret < 0) {
>>>>>>        return ret;
>>>>>> }
>>>>>
>>>>> Personally I prefer the other approach -- this one has an extra line
>>>>> in the source and
>>>>> needs an extra local variable.
>>>>
>>>> Me too, except when func(...) is so long that
>>>>
>>>>       if (func(...) < 0) {
>>>>
>>>> becomes illegible like
>>>>
>>>>       if (func(... yadda, yadda,
>>>>                yadda, ...) < 0) {
>>>>
>>>> Then an extra variable can improve things.
>>>>
>>>>>> Are you sure that adding a lot of boolean functions is a good idea? I somehow feel better with more usual int functions with -errno on failure.
>>>>>>
>>>>>> Bool is a good return value for functions which are boolean by nature: checks, is something correspond to some criteria. But for reporting an error I'd prefer -errno.
>>>>>
>>>>> When would we want to return an errno? I thought the whole point of the
>>>>> Error* was that that was where information about the error was returned.
>>>>> If all your callsites are just going to do "if (ret < 0) { ... } then having
>>>>> the functions pick an errno value to return is just extra work.
>>>>
>>>> 0/-1 vs. true/false is a matter of convention.  Lacking convention, it's
>>>> a matter of taste. >
>>>> 0/-1 vs. 0/-errno depends on the function and its callers.  When -errno
>>>> enables callers to distinguish failures in a sane and simple way, use
>>>> it.  When -errno feels "natural", I'd say feel free to use it even when
>>>> all existing callers only check < 0.
>>>>
>>>> When you return non-null/null or true/false on success/error, neglecting
>>>> to document that in a function contract can perhaps be tolerated; you're
>>>> using the return type the common, obvious way.  But when you return 0/-1
>>>> or 0/-errno, please spell it out.  I've seen too many "Operation not
>>>> permitted" that were actually -1 mistaken for -EPERM.  Also too many
>>>> functions that return -1 for some failures and -errno for others.
>>>>
>>>
>>> I just want to add one note:
>>>
>>> OK, you like the pattern
>>>
>>>    if (func()) {
>>>        <handle error>
>>>    }
>>>
>>> , I can live with it.
>>>
>>> I believe, we have a lot of such patterns already in code.
>>>
>>> Now, we are going to add a lot of functions, returning true on success and false on failure, so add a lot of patterns
>>>
>>>    if (!func()) {
>>>        <handle error>
>>>    }
>>
>> We already have this pattern all over the place with functions returning
>> non-null pointers on success, null pointer on failure.
>
> Functions returning pointer are simpler to distinguish by name.
>
> Hmm, strange. But this pattern lose the pointer.. You mean
>
> ptr = func();
> if (!ptr) {
>   <handle error>
> }

Yes, when you actually need the pointer.  Common, but not universal.

> this is much more understandable. Usually ptr variable name and function name - all will help to understand that it's all about pointer.
>
>
>>
>>> ---
>>>
>>> After it, looking at something like
>>>
>>>    if (!func()) {} / if (func()) {}
>>>
>>> I'll have to always jump to function definition, to check is it int or bool function, to understand what exactly is meant and is there a mistake in the code..
>>> So, I'm afraid that such conversion will not help reviewing/understanding the code. I'd prefer to avoid using two opposite conventions in on project.
>>
>> C sucks :)
>>
>> Conventions help mitigate.  Here's one: when fun() returns
>> non-negative/negative on success/error, always use
>>
>>      fun(...) < 0
>
> This reduces chances that it fit in one line..

Yes, that's a drawback.

> But yes, if all use this convention, it makes it obvious what happening.
>
>>
>> or
>>
>>      fun(...) >= 0
>>
>> to check.  I dislike the latter.
>>
>> When returns 0/negative, always use
>>
>>      fun(...) < 0
>>
>> Avoid
>>
>>      fun(...) >= 0
>>
>> because that suggests it could return a positive value, which is wrong.
>>
>> Avoid
>>
>>      fun(...)
>>
>> because that requires the reader to know the return type.
>
> Exactly the problem I mention. To follow your suggestion, we'll have to update
> the whole code base.. However, why not.

Only if we value the consistency more than the update labor and churn.

>>
>> When its returns non-null/null or true/false on success/failure, always
>> use
>>
>>      !fun(...)
>>
>> Avoid
>>
>>      fun(...)
>>
>> because that requires the reader to know the return type.
>>
>> Note that we have a usable check for failure in all cases.  Goes well
>> with the habit to handle errors like this whenever practical
>>
>>      if (failed) {
>>          handle failure
>>          bail out
>>      }
>>      handle success
>>
>
> OK. If convert everything to your suggestion it looks good.
>
> The only possible problem is boolean functions, which just check something, not returning the error..
>
> With a function like is_x_in_set(x, set), it's native to write
>
> if (is_x_in_set(x, set)) {
>
>    ...
>
> }
>
> which is a bit in conflict with your suggestion. Still, such functions should be simply distinguished by name.

The conventions I described only apply to error checking.  I certainly
don't mean to discourage things like

    while (*p && qemu_isspace(*p)) {
        p++;
    }

Also, they're *conventions*, not law.  The purpose of conventions is to
help us write clearer code.  Don't make code less clear just to conform
to a convention.  Use your judgement.

>>> I can also imagine combining different function types (int/bool) in if conditions o_O, what will save us from it?
>>
>> Can you give an example?
>
> I just meant something like if (f1() || !f2()) { < error > }, nothing special.

Perfectly consistent error checking style, idiomatic C, pick one.

>>> And don't forget about bool functions, which just check something, and false is not an error, but just negative answer on some question.
>>
>> For what it's worth, GLib specifically advises against such function
>> contracts:
>>
>>      By convention, if you return a boolean value indicating success then
>>      TRUE means success and FALSE means failure.  Avoid creating
>>      functions which have a boolean return value and a GError parameter,
>>      but where the boolean does something other than signal whether the
>>      GError is set.  Among other problems, it requires C callers to
>>      allocate a temporary error.  Instead, provide a "gboolean *" out
>>      parameter.  There are functions in GLib itself such as
>>      g_key_file_has_key() that are deprecated because of this.
>>
>
> Sounds good. But we are speaking about all functions, not only with errp parameter.. Or not?

The conventions I described apply to error checking, regardless of Error
object use.  For instance, they'd apply to error-checking
remove("some-file").



  reply	other threads:[~2020-04-03  7:49 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-01  9:02 Questionable aspects of QEMU Error's design Markus Armbruster
2020-04-01 12:10 ` Vladimir Sementsov-Ogievskiy
2020-04-01 12:14   ` Vladimir Sementsov-Ogievskiy
2020-04-01 14:01   ` Alex Bennée
2020-04-01 15:49     ` Markus Armbruster
2020-04-01 15:05   ` Markus Armbruster
2020-04-01 12:44 ` Daniel P. Berrangé
2020-04-01 12:47   ` Vladimir Sementsov-Ogievskiy
2020-04-01 15:34   ` Markus Armbruster
2020-04-01 20:15 ` Peter Maydell
2020-04-02  5:31   ` Vladimir Sementsov-Ogievskiy
2020-04-02  9:36     ` BALATON Zoltan
2020-04-02 14:11       ` Vladimir Sementsov-Ogievskiy
2020-04-02 14:34         ` Markus Armbruster
2020-04-02 15:28           ` BALATON Zoltan
2020-04-03  7:09             ` Markus Armbruster
2020-04-02  5:54   ` Markus Armbruster
2020-04-02  6:11     ` Vladimir Sementsov-Ogievskiy
2020-04-02  8:11       ` Peter Maydell
2020-04-02  8:49         ` Daniel P. Berrangé
2020-04-02  8:55         ` Markus Armbruster
2020-04-02 14:35           ` Vladimir Sementsov-Ogievskiy
2020-04-02 15:06             ` Markus Armbruster
2020-04-02 17:17               ` Vladimir Sementsov-Ogievskiy
2020-04-03  7:48                 ` Markus Armbruster [this message]
2020-04-02 18:57           ` Paolo Bonzini
2020-04-02  8:47     ` Daniel P. Berrangé
2020-04-02  9:19       ` Alex Bennée
2020-04-02 14:33     ` Eric Blake
2020-04-04  7:59 ` Markus Armbruster
2020-04-04 10:59   ` Markus Armbruster
2020-04-06 14:05     ` Eduardo Habkost
2020-04-06 14:38       ` Eduardo Habkost
2020-04-06 14:10     ` Daniel P. Berrangé
2020-04-27 15:36   ` Markus Armbruster
2020-04-28  5:20     ` Vladimir Sementsov-Ogievskiy
2020-05-14  7:59       ` Vladimir Sementsov-Ogievskiy
2020-05-15  4:28         ` Markus Armbruster
2020-07-03  7:38           ` Markus Armbruster
2020-07-03  9:07             ` Vladimir Sementsov-Ogievskiy
2020-07-03 12:21   ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87imihrovy.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=vsementsov@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.