[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