All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: "Marc-André Lureau" <marcandre.lureau@gmail.com>
Cc: Markus Armbruster <armbru@redhat.com>,
	QEMU <qemu-devel@nongnu.org>, Paolo Bonzini <pbonzini@redhat.com>,
	Gerd Hoffmann <kraxel@redhat.com>,
	"Dr. David Alan Gilbert" <dgilbert@redhat.com>,
	Michael Roth <mdroth@linux.vnet.ibm.com>
Subject: Re: [Qemu-devel] [PATCH v6 07/15] qapi: add #if/#endif helpers
Date: Tue, 03 Jul 2018 17:34:00 +0200	[thread overview]
Message-ID: <878t6s72qf.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <CAJ+F1CL8BHh6K5Ds_AdnGAwuCHH-HoAAi+GLQMGGuNLMHfq1Nw@mail.gmail.com> (=?utf-8?Q?=22Marc-Andr=C3=A9?= Lureau"'s message of "Tue, 3 Jul 2018 17:08:08 +0200")

Marc-André Lureau <marcandre.lureau@gmail.com> writes:

> Hi
>
> On Tue, Jul 3, 2018 at 3:37 PM, Markus Armbruster <armbru@redhat.com> wrote:
>> Marc-André Lureau <marcandre.lureau@gmail.com> writes:
>>
>>> Hi
>>>
>>> On Tue, Jul 3, 2018 at 1:53 PM, Markus Armbruster <armbru@redhat.com> wrote:
>>>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>>>
>>>>> Add helpers to wrap generated code with #if/#endif lines.
>>>>>
>>>>> Add QAPIGenCSnippet class to write C snippet code, make QAPIGenC
>>>>> inherit from it, for full C files with copyright headers etc.
>>>>
>>>> It's called QAPIGenCCode now.  Can touch up when I apply, along with
>>>> another instance below.
>>>>
>>>> Leaves the reader wondering *why* you need to splice QAPIGenCCode
>>>> between QAPIGen and QAPIGenC.  Becomes clear only in PATCH 10.
>>>> Providing the class now reduces code churn, but you should explain why.
>>>> Perhaps:
>>>>
>>>>   A later patch wants to use QAPIGen for generating C snippets rather
>>>>   than full C files with copyright headers etc.  Splice in class
>>>>   QAPIGenCCode between QAPIGen and QAPIGenC.
>>>
>>> sure, thanks
>>>
>>>>
>>>>> Add a 'with' statement context manager that will be used to wrap
>>>>> generator visitor methods.  The manager will check if code was
>>>>> generated before adding #if/#endif lines on QAPIGenCSnippet
>>>>> objects. Used in the following patches.
>>>>>
>>>>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>>>> ---
>>>>>  scripts/qapi/common.py | 101 +++++++++++++++++++++++++++++++++++++++--
>>>>>  1 file changed, 97 insertions(+), 4 deletions(-)
>>>>>
>>>>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
>>>>> index 1b56065a80..44eaf25850 100644
>>>>> --- a/scripts/qapi/common.py
>>>>> +++ b/scripts/qapi/common.py
>>>>> @@ -12,6 +12,7 @@
>>>>>  # See the COPYING file in the top-level directory.
>>>>>
>>>>>  from __future__ import print_function
>>>>> +from contextlib import contextmanager
>>>>>  import errno
>>>>>  import os
>>>>>  import re
>>>>> @@ -1974,6 +1975,40 @@ def guardend(name):
>>>>>                   name=guardname(name))
>>>>>
>>>>>
>>>>> +def gen_if(ifcond):
>>>>> +    ret = ''
>>>>> +    for ifc in ifcond:
>>>>> +        ret += mcgen('''
>>>>> +#if %(cond)s
>>>>> +''', cond=ifc)
>>>>> +    return ret
>>>>> +
>>>>> +
>>>>> +def gen_endif(ifcond):
>>>>> +    ret = ''
>>>>> +    for ifc in reversed(ifcond):
>>>>> +        ret += mcgen('''
>>>>> +#endif /* %(cond)s */
>>>>> +''', cond=ifc)
>>>>> +    return ret
>>>>> +
>>>>> +
>>>>> +def wrap_ifcond(ifcond, before, after):
>>>>
>>>> Looks like a helper method.  Name it _wrap_ifcond?
>>>
>>> Not fond of functions with _. Iirc, it was suggested by a linter to
>>> make it a top-level function. I don't mind if you change it on commit.
>>
>> It's just how Python defines a module's public names.  I don't like the
>> proliferation of leading underscores either, but I feel it's best to go
>> with the flow here.
>
> ok
>
>>
>>>>> +    if ifcond is None or before == after:
>>>>> +        return after
>>>>
>>>> The before == after part suppresses empty conditionals.  Suggest
>>>>
>>>>            return after            # suppress empty #if ... #endif
>>>>
>>>> The ifcond is None part is suppresses "non-conditionals".  How can this
>>>> happen?  If it can't, let's drop this part.
>>>
>>> because the function can be called without additional checks on ifcond
>>> is None. I don't see what would be the benefit in moving this to the
>>> caller responsibility.
>>
>> Why stop at None?  Why not empty string?  Empty containers other than
>> []?  False?  Numeric zero?
>>
>> To answer my own question: because a precise function signatures reduce
>> complexity.  "The first argument is a list of strings, no ifs, no buts"
>> is simpler than "the first argument may be None, or a list of I don't
>> remember what exactly, but if you screw it up, the function hopefully
>> throws up before it does real damage" ;)
>
> So you prefer if not ifcond or before == after ? ok

I'd recommend

      +    if before == after:
      +        return after

If we pass crap, we die in gen_if()'s for ifc in ifcond.  The difference
is now crap includes None.

>>>> Another non-conditional is [].  See below.
>>>>
>>>>> +
>>>>> +    assert after.startswith(before)
>>>>> +    out = before
>>>>> +    added = after[len(before):]
>>>>> +    if added[0] == '\n':
>>>>> +        out += '\n'
>>>>> +        added = added[1:]
>>>>
>>>> The conditional adjusts vertical space around #if.  This might need
>>>> tweaking, but let's not worry about that now.
>>>>
>>>>> +    out += gen_if(ifcond)
>>>>> +    out += added
>>>>> +    out += gen_endif(ifcond)
>>>>
>>>> There's no similar adjustment for #endif.  Again, let's not worry about
>>>> that now.
>>>>
>>>>> +    return out
>>>>> +
>>>>> +
>>>>
>>>> Since gen_if([]) and gen_endif([]) return '', wrap_ifcond([], before,
>>>> after) returns after.  Okay.
>>>>
>>>>>  def gen_enum_lookup(name, values, prefix=None):
>>>>>      ret = mcgen('''
>>>>>
>>>>> @@ -2064,6 +2099,7 @@ class QAPIGen(object):
>>>>>      def __init__(self):
>>>>>          self._preamble = ''
>>>>>          self._body = ''
>>>>> +        self._start_if = None
>>>>>
>>>>>      def preamble_add(self, text):
>>>>>          self._preamble += text
>>>>> @@ -2071,6 +2107,25 @@ class QAPIGen(object):
>>>>>      def add(self, text):
>>>>>          self._body += text
>>>>>
>>>>> +    def start_if(self, ifcond):
>>>>> +        assert self._start_if is None
>>>>> +
>>>>
>>>> Let's drop this blank line.
>>>
>>> I prefer to have preconditions separated from the code, but ok
>>
>> I sympathize, but not if both are one-liners.
>
> ok
>
>>
>>>>
>>>>> +        self._start_if = (ifcond, self._body, self._preamble)
>>>>
>>>> It's now obvious that you can't nest .start_if() ... .endif().  Good.
>>>>
>>>>> +
>>>>> +    def _wrap_ifcond(self):
>>>>> +        pass
>>>>> +
>>>>> +    def end_if(self):
>>>>> +        assert self._start_if
>>>>> +
>>>>
>>>> Let's drop this blank line.
>>>>
>>>>> +        self._wrap_ifcond()
>>>>> +        self._start_if = None
>>>>> +
>>>>> +    def get_content(self, fname=None):
>>>>> +        assert self._start_if is None
>>>>> +        return (self._top(fname) + self._preamble + self._body
>>>>> +                + self._bottom(fname))
>>>>> +
>>>>>      def _top(self, fname):
>>>>>          return ''
>>>>>
>>>>
>>>> Note for later: all this code has no effect unless ._wrap_ifcond() is
>>>> overridden.
>>>>
>>>>> @@ -2091,8 +2146,7 @@ class QAPIGen(object):
>>>>>              f = open(fd, 'r+', encoding='utf-8')
>>>>>          else:
>>>>>              f = os.fdopen(fd, 'r+')
>>>>> -        text = (self._top(fname) + self._preamble + self._body
>>>>> -                + self._bottom(fname))
>>>>> +        text = self.get_content(fname)
>>>>>          oldtext = f.read(len(text) + 1)
>>>>>          if text != oldtext:
>>>>>              f.seek(0)
>>>>> @@ -2101,10 +2155,49 @@ class QAPIGen(object):
>>>>>          f.close()
>>>>>
>>>>>
>>>>> -class QAPIGenC(QAPIGen):
>>>>> +@contextmanager
>>>>> +def ifcontext(ifcond, *args):
>>>>> +    """A 'with' statement context manager to wrap with start_if()/end_if()
>>>>>
>>>>> -    def __init__(self, blurb, pydoc):
>>>>> +    *args: variable length argument list of QAPIGen
>>>>
>>>> Perhaps: any number of QAPIGen objects
>>>>
>>>
>>> sure
>>>
>>>>> +
>>>>> +    Example::
>>>>> +
>>>>> +        with ifcontext(ifcond, self._genh, self._genc):
>>>>> +            modify self._genh and self._genc ...
>>>>> +
>>>>> +    Is equivalent to calling::
>>>>> +
>>>>> +        self._genh.start_if(ifcond)
>>>>> +        self._genc.start_if(ifcond)
>>>>> +        modify self._genh and self._genc ...
>>>>> +        self._genh.end_if()
>>>>> +        self._genc.end_if()
>>>>> +
>>>>
>>>> Can we drop this blank line?
>>>>
>>>
>>> I guess we can, I haven't tested the rst formatting this rigorously.
>>> Hopefully the linter does it.
>>>
>>>>> +    """
>>>>> +    for arg in args:
>>>>> +        arg.start_if(ifcond)
>>>>> +    yield
>>>>> +    for arg in args:
>>>>> +        arg.end_if()
>>>>> +
>>>>> +
>>>>> +class QAPIGenCCode(QAPIGen):
>>>>> +
>>>>> +    def __init__(self):
>>>>>          QAPIGen.__init__(self)
>>>>> +
>>>>> +    def _wrap_ifcond(self):
>>>>> +        self._body = wrap_ifcond(self._start_if[0],
>>>>> +                                 self._start_if[1], self._body)
>>>>> +        self._preamble = wrap_ifcond(self._start_if[0],
>>>>> +                                     self._start_if[2], self._preamble)
>>>>
>>>> Can you explain why you put the machinery for conditionals in QAPIGen
>>>> and not QAPIGenCCode?
>>>
>>> Iirc, this has to do with the fact that QAPIGenDoc is used for
>>> visiting, and thus QAPIGen start_if()/end_if() could be handled there,
>>
>> Can you point me to calls of .start_if(), .end_if(), .get_content() for
>> anything but QAPIGenCCode and its subtypes?
>>
>>> while we only want to generate #ifdef wrapping in QAPIGenCCode. I
>>> guess I could move more of start_if()/end_if() data to QAPIGenCCode
>>> (make them virtual / 'pass' in QAPIGen) Is that what you would like to
>>> see?
>>
>> If there are no such calls, we should simply move the whole shebang to
>> QAPIGenCCode.
>
> done

Does that mean I should expect v7 from you?

>> If there are such calls, the common supertype QAPIGen has to provide the
>> methods.
>>
>> If we expect subtypes other than QAPIGenCCode to implement conditional
>> code generation the same way, except for a different ._wrap_ifcond(),
>> then the patch is okay as is.
>>
>> If we don't, the transformation you described looks like an improvement
>> to me, because it keeps the actual code closer together.
>>
>> What's your take on it?
>>
>> I think I could make the transformation when I apply.
>>
>>>>
>>>>> +
>>>>> +
>>>>> +class QAPIGenC(QAPIGenCCode):
>>>>> +
>>>>> +    def __init__(self, blurb, pydoc):
>>>>> +        QAPIGenCCode.__init__(self)
>>>>>          self._blurb = blurb
>>>>>          self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
>>>>>                                                    re.MULTILINE))
>>>>

  reply	other threads:[~2018-07-03 15:34 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-27 16:35 [Qemu-devel] [PATCH v6 00/15] qapi: add #if pre-processor conditions to generated code (part 1) Marc-André Lureau
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 01/15] qapi: add 'if' to top-level expressions Marc-André Lureau
2018-06-28 14:57   ` Markus Armbruster
2018-06-28 15:34     ` Marc-André Lureau
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 02/15] tests Marc-André Lureau
2018-06-27 16:39   ` Marc-André Lureau
2018-06-28 15:35     ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 03/15] qapi: pass 'if' condition into QAPISchemaEntity objects Marc-André Lureau
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 04/15] qapi: leave the ifcond attribute undefined until check() Marc-André Lureau
2018-06-28 17:45   ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 05/15] qapi: add 'ifcond' to visitor methods Marc-André Lureau
2018-06-28 17:48   ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 06/15] qapi: mcgen() shouldn't indent # lines Marc-André Lureau
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 07/15] qapi: add #if/#endif helpers Marc-André Lureau
2018-07-03 11:53   ` Markus Armbruster
2018-07-03 12:35     ` Marc-André Lureau
2018-07-03 13:37       ` Markus Armbruster
2018-07-03 15:08         ` Marc-André Lureau
2018-07-03 15:34           ` Markus Armbruster [this message]
2018-07-03 15:43             ` Marc-André Lureau
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 08/15] qapi-introspect: modify to_qlit() to append ', ' on level > 0 Marc-André Lureau
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 09/15] qapi-introspect: add preprocessor conditions to generated QLit Marc-André Lureau
2018-07-03 12:09   ` Markus Armbruster
2018-07-03 13:11     ` Marc-André Lureau
2018-07-03 13:47       ` Markus Armbruster
2018-07-03 15:08         ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 10/15] qapi/commands: add #if conditions to commands Marc-André Lureau
2018-07-03 14:43   ` Markus Armbruster
2018-07-03 15:09     ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 11/15] qapi/events: add #if conditions to events Marc-André Lureau
2018-07-03 14:47   ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 12/15] qapi-types: add #if conditions to types & visitors Marc-André Lureau
2018-07-03 14:50   ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 13/15] qapi: add 'If:' section to generated documentation Marc-André Lureau
2018-07-03 14:59   ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 14/15] qapi: add conditions to VNC type/commands/events on the schema Marc-André Lureau
2018-07-03 15:04   ` Markus Armbruster
2018-06-27 16:35 ` [Qemu-devel] [PATCH v6 15/15] qapi: add conditions to SPICE " Marc-André Lureau
2018-07-03 15:05   ` 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=878t6s72qf.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=kraxel@redhat.com \
    --cc=marcandre.lureau@gmail.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /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.