[RFC 00/21] RFC: Generate parsexml/formatbuf functions based on directives

Daniel P. Berrangé berrange at redhat.com
Fri Jul 3 13:00:03 UTC 2020


On Wed, Jul 01, 2020 at 12:06:32AM +0800, Shi Lei wrote:
> >On Wed, Jun 10, 2020 at 09:20:28AM +0800, Shi Lei wrote:
> >>
> >> Last RFC: [https://www.redhat.com/archives/libvir-list/2020-April/msg00970.html]
> >> In last RFC, I suggested we can generate object-model code based on relax-ng
> >> files and Daniel gave it some comments.
> >>
> >> Follow the suggestion from Daniel, I make another try to generate parsexml/formatbuf
> >> functions automatically.
> >>
> >>
> >> ============
> >>  Directives
> >> ============
> >>
> >> Still, we need several directives that can direct a tool to generate functions.
> >> These directives work on the declarations of structs. For example:
> >>
> >>     typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef;
> >>     typedef virNetworkDNSTxtDef *virNetworkDNSTxtDefPtr;
> >>     struct _virNetworkDNSTxtDef {   /* genparse:concisehook, genformat */
> >>         char *name;                 /* xmlattr, required */
> >>         char *value;                /* xmlattr */
> >>     };
> >>
> >>     typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef;
> >>     typedef virNetworkDNSSrvDef *virNetworkDNSSrvDefPtr;
> >>     struct _virNetworkDNSSrvDef {   /* genparse:withhook, genformat */
> >>         char *service;              /* xmlattr */
> >>         char *protocol;             /* xmlattr */
> >>         char *domain;               /* xmlattr */
> >>         char *target;               /* xmlattr */
> >>         unsigned int port;          /* xmlattr */
> >>         unsigned int priority;      /* xmlattr */
> >>         unsigned int weight;        /* xmlattr */
> >>     };
> >>
> >>     typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef;
> >>     typedef virNetworkDNSHostDef *virNetworkDNSHostDefPtr;
> >>     struct _virNetworkDNSHostDef {  /* genparse:withhook, genformat */
> >>         virSocketAddr ip;           /* xmlattr */
> >>         size_t nnames;
> >>         char **names;               /* xmlelem:hostname, array */
> >>     };
> >>
> >>     typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder;
> >>     typedef virNetworkDNSForwarder *virNetworkDNSForwarderPtr;
> >>     struct _virNetworkDNSForwarder {    /* genparse:withhook, genformat */
> >>         char *domain;                   /* xmlattr */
> >>         virSocketAddr addr;             /* xmlattr */
> >>     };
> >>
> >>     typedef struct _virNetworkDNSDef virNetworkDNSDef;
> >>     typedef virNetworkDNSDef *virNetworkDNSDefPtr;
> >>     struct _virNetworkDNSDef {              /* genparse:withhook, genformat */
> >>         virTristateBool enable;                 /* xmlattr */
> >>         virTristateBool forwardPlainNames;      /* xmlattr */
> >>         size_t nforwarders;
> >>         virNetworkDNSForwarderPtr forwarders;   /* xmlelem, array */
> >>         size_t ntxts;
> >>         virNetworkDNSTxtDefPtr txts;            /* xmlelem, array */
> >>         size_t nsrvs;
> >>         virNetworkDNSSrvDefPtr srvs;            /* xmlelem, array */
> >>         size_t nhosts;
> >>         virNetworkDNSHostDefPtr hosts;          /* xmlelem, array */
> >>     };
> >>
> >>
> >> Explanation for these directives:
> >>
> >>
> >>     - genparse[:withhook|concisehook]
> >>
> >>       Only work on a struct.
> >>       Generate parsexml function for this struct only if 'genparse' is specified.
> >>       The function name is based on the struct-name and suffixed with 'ParseXML'.
> >>       E.g., for struct virNetworkDNSDef, its parsexml function is
> >>       virNetworkDNSDefParseXML.
> >>
> >>       If a parsexml function includes some error-checking code, it needs a
> >>       post-process hook to hold them. Specify 'withhook' or 'concisehook' to
> >>       setup a hook point in the parsexml function.
> >>
> >>       Executing the tool manually can show the declaration of hook function.
> >>       E.g. check declaration of 'virNetworkDNSDefParseXMLHook'.
> >>
> >>         # ./build-aux/generator/go show virNetworkDNSDef -kp
> >>
> >>         int
> >>         virNetworkDNSDefParseXMLHook(xmlNodePtr node,
> >>                                      virNetworkDNSDefPtr def,
> >>                                      const char *instname,
> >>                                      void *opaque,
> >>                                      const char *enableStr,
> >>                                      const char *forwardPlainNamesStr,
> >>                                      int nForwarderNodes,
> >>                                      int nTxtNodes,
> >>                                      int nSrvNodes,
> >>                                      int nHostNodes);
> >>
> >>       Some helper arguments (such as 'enableStr', 'nTxtNodes', etc.) are
> >>       passed in to indicate the existence of fields.
> >>       If these arguments are useless, specify 'concisehook' to omit them.
> >>
> >>       When 'genparse' is specified, clear function for this struct is also
> >>       generated implicitly, it is because the generated parsexml function needs
> >>       to call the clear function.
> >>
> >>
> >>     - genformat
> >>
> >>       Only work on a struct.
> >>       Generate formatbuf function for this struct only if 'genformat' is specified.
> >>       The function name is based on struct-name and suffixed with 'FormatBuf'.
> >>
> >>
> >>     - xmlattr[:thename]
> >>
> >>       Parse/Format the field as an XML attribute.
> >>       If 'thename' is specified, use it as the XML attribute name;
> >>       or use the filed name.
> >>
> >>
> >>     - xmlelem[:thename]
> >>      
> >>       Parse/Format the field as a child struct.
> >>       If 'thename' is specified, use it as the XML element name;
> >>       or use the filed name.
> >>
> >>
> >>     - array
> >>
> >>       Parse/Format the field as an array.
> >>       Its related field is a counter, which name should follow the pattern:
> >>       n + 'field_name'.
> >>
> >>
> >>     - required
> >>
> >>       The field must have a corresponding item defined in XML.
> >
> >This all looks pretty reasonable and simple to understand to me.
> >
> >I think the complex one is going to arrive when we need to deal with
> >discriminated unions. eg the   <forward mode='passthrough|nat|hostdev'>
> >where the @mode attribute determines which child struct and elements
> >are permitted. 
> 
> I suggest we can now check this in a hook function.
> If we deal with discriminated unions, I think we should introduce some directives
> or rules to indicate the relationship between union values and child struct.
> I'm afraid that the generator and the new directives will be too complicated.
> 
> Do you have any suggestion about this?
The current layout of many of our structs is unhelpful, because while we
treat the elements contents as a union, the struct is often flat. I think
we'll need to fix this as part of adopting the generator, so that we always
use a union for structs.

Taking one of the more simple examples

  struct _virDomainHostdevSubsys {
    int type; /* enum virDomainHostdevSubsysType */
    union {
        virDomainHostdevSubsysUSB usb;
        virDomainHostdevSubsysPCI pci;
        virDomainHostdevSubsysSCSI scsi;
        virDomainHostdevSubsysSCSIVHost scsi_host;
        virDomainHostdevSubsysMediatedDev mdev;
    } u;
  };


The "int type" field is the union discriminator and has a string
conversion via the enum.

So we can annotate the union to say which field to use as the
discriminator to switch parsing based on:

  struct _virDomainHostdevSubsys {
    virDomainHostdevSubsysType type;  /* xmlattr */
    union {
        virDomainHostdevSubsysUSB usb;
        virDomainHostdevSubsysPCI pci;
        virDomainHostdevSubsysSCSI scsi;
        virDomainHostdevSubsysSCSIVHost scsi_host;
        virDomainHostdevSubsysMediatedDev mdev;
    } u; /* xmlswitch:type */
  };

We can declare that the name of each union member should match the string
form of the "type" attribute. eg with  type="usb", we expect the union to
have a field "usb". So with type="usb", we'll parse the virDomainHostdevSubsysUSB
struct as the content.


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|




More information about the libvir-list mailing list