[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH RFC v3 18/20] qapi: Plumb in 'box' to qapi generator
From: |
Eric Blake |
Subject: |
[Qemu-devel] [PATCH RFC v3 18/20] qapi: Plumb in 'box' to qapi generator lower levels |
Date: |
Tue, 18 Aug 2015 09:05:16 -0700 |
A future patch will add support for passing a qapi union
type as the 'data' of a command. But to do that, the user
function for implementing the command, as called by the
generated marshal command, must take the corresponding C
struct as a single boxed pointer, rather than a breakdown
into one parameter per member. This patch adds the
internal plubming of a 'box' flag associated with each
command and event. For this patch, no behavior changes,
other than the testsuite outputting the value of the new
flag (always False for now).
Signed-off-by: Eric Blake <address@hidden>
---
scripts/qapi-commands.py | 93 ++++++++++++++++++---------------
scripts/qapi-event.py | 65 ++++++++++++-----------
scripts/qapi-introspect.py | 4 +-
scripts/qapi.py | 47 ++++++++++-------
tests/qapi-schema/args-member-array.out | 2 +-
tests/qapi-schema/event-case.out | 1 +
tests/qapi-schema/ident-with-escape.out | 2 +-
tests/qapi-schema/indented-expr.out | 4 +-
tests/qapi-schema/qapi-schema-test.out | 17 +++---
tests/qapi-schema/returns-int.out | 2 +-
tests/qapi-schema/test-qapi.py | 8 +--
11 files changed, 139 insertions(+), 106 deletions(-)
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 7ff7c31..e2b8f7a 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -15,13 +15,13 @@
from qapi import *
import re
-def gen_command_decl(name, arg_type, ret_type):
+def gen_command_decl(name, arg_type, box, ret_type):
return mcgen('''
%(c_type)s qmp_%(c_name)s(%(params)s);
''',
c_type=(ret_type and ret_type.c_type()) or 'void',
c_name=c_name(name),
- params=gen_params(arg_type, 'Error **errp'))
+ params=gen_params(arg_type, box, 'Error **errp'))
def gen_err_check(err):
if not err:
@@ -33,15 +33,18 @@ if (%(err)s) {
''',
err=err)
-def gen_call(name, arg_type, ret_type):
+def gen_call(name, arg_type, box, ret_type):
ret = ''
argstr = ''
- if arg_type:
- for memb in arg_type.members:
- if memb.optional:
- argstr += 'has_%s, ' % c_name(memb.name)
- argstr += '%s, ' % c_name(memb.name)
+ if box:
+ assert False # not implemented
+ else:
+ if arg_type:
+ for memb in arg_type.members:
+ if memb.optional:
+ argstr += 'has_%s, ' % c_name(memb.name)
+ argstr += '%s, ' % c_name(memb.name)
lhs = ''
if ret_type:
@@ -63,7 +66,7 @@ qmp_marshal_output_%(c_name)s(retval, ret, &local_err);
pop_indent()
return ret
-def gen_marshal_vars(arg_type, ret_type):
+def gen_marshal_vars(arg_type, box, ret_type):
ret = mcgen('''
Error *local_err = NULL;
''')
@@ -83,18 +86,21 @@ QapiDeallocVisitor *md;
Visitor *v;
''')
- for memb in arg_type.members:
- if memb.optional:
- ret += mcgen('''
+ if box:
+ assert False # not implemented
+ else:
+ for memb in arg_type.members:
+ if memb.optional:
+ ret += mcgen('''
bool has_%(c_name)s = false;
''',
- c_name=c_name(memb.name))
- ret += mcgen('''
+ c_name=c_name(memb.name))
+ ret += mcgen('''
%(c_type)s %(c_name)s = %(c_null)s;
''',
- c_name=c_name(memb.name),
- c_type=memb.type.c_type(),
- c_null=memb.type.c_null())
+ c_name=c_name(memb.name),
+ c_type=memb.type.c_type(),
+ c_null=memb.type.c_null())
ret += '\n'
else:
ret += mcgen('''
@@ -105,7 +111,7 @@ bool has_%(c_name)s = false;
pop_indent()
return ret
-def gen_marshal_input_visit(arg_type, dealloc=False):
+def gen_marshal_input_visit(arg_type, box, dealloc=False):
ret = ''
if not arg_type:
@@ -128,28 +134,31 @@ v = qapi_dealloc_get_visitor(md);
v = qmp_input_get_visitor(mi);
''')
- for memb in arg_type.members:
- if memb.optional:
- ret += mcgen('''
+ if box:
+ assert False # not implemented
+ else:
+ for memb in arg_type.members:
+ if memb.optional:
+ ret += mcgen('''
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
''',
- c_name=c_name(memb.name), name=memb.name,
- errp=errparg)
- ret += gen_err_check(errarg)
- ret += mcgen('''
+ c_name=c_name(memb.name), name=memb.name,
+ errp=errparg)
+ ret += gen_err_check(errarg)
+ ret += mcgen('''
if (has_%(c_name)s) {
''',
- c_name=c_name(memb.name))
- push_indent()
- ret += mcgen('''
+ c_name=c_name(memb.name))
+ push_indent()
+ ret += mcgen('''
visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s);
''',
- c_name=c_name(memb.name), name=memb.name,
- c_type=memb.type.c_name(), errp=errparg)
- ret += gen_err_check(errarg)
- if memb.optional:
- pop_indent()
- ret += mcgen('''
+ c_name=c_name(memb.name), name=memb.name,
+ c_type=memb.type.c_name(), errp=errparg)
+ ret += gen_err_check(errarg)
+ if memb.optional:
+ pop_indent()
+ ret += mcgen('''
}
''')
@@ -200,7 +209,7 @@ def gen_marshal_decl(name):
''',
proto=gen_marshal_proto(name))
-def gen_marshal(name, arg_type, ret_type):
+def gen_marshal(name, arg_type, box, ret_type):
ret = mcgen('''
%(proto)s
@@ -208,9 +217,9 @@ def gen_marshal(name, arg_type, ret_type):
''',
proto=gen_marshal_proto(name))
- ret += gen_marshal_vars(arg_type, ret_type)
- ret += gen_marshal_input_visit(arg_type)
- ret += gen_call(name, arg_type, ret_type)
+ ret += gen_marshal_vars(arg_type, box, ret_type)
+ ret += gen_marshal_input_visit(arg_type, box)
+ ret += gen_call(name, arg_type, box, ret_type)
if re.search('^ *goto out;', ret, re.MULTILINE):
ret += mcgen('''
@@ -220,7 +229,7 @@ out:
ret += mcgen('''
error_propagate(errp, local_err);
''')
- ret += gen_marshal_input_visit(arg_type, dealloc=True)
+ ret += gen_marshal_input_visit(arg_type, box, dealloc=True)
ret += mcgen('''
}
''')
@@ -271,16 +280,16 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
self.regy = None
self.visited_ret_types = None
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, box):
if not gen:
return
- self.decl += gen_command_decl(name, arg_type, ret_type)
+ self.decl += gen_command_decl(name, arg_type, box, ret_type)
if ret_type and ret_type not in self.visited_ret_types:
self.visited_ret_types.add(ret_type)
self.defn += gen_marshal_output(ret_type)
if middle_mode:
self.decl += gen_marshal_decl(name)
- self.defn += gen_marshal(name, arg_type, ret_type)
+ self.defn += gen_marshal(name, arg_type, box, ret_type)
if not middle_mode:
self.regy += gen_register_command(name, success_response)
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index d100099..278cd7f 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -13,19 +13,19 @@
from qapi import *
-def gen_event_send_proto(name, arg_type):
+def gen_event_send_proto(name, arg_type, box):
return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
'c_name': c_name(name.lower()),
- 'param': gen_params(arg_type, 'Error **errp')}
+ 'param': gen_params(arg_type, box, 'Error **errp')}
-def gen_event_send_decl(name, arg_type):
+def gen_event_send_decl(name, arg_type, box):
return mcgen('''
%(proto)s;
''',
- proto=gen_event_send_proto(name, arg_type))
+ proto=gen_event_send_proto(name, arg_type, box))
-def gen_event_send(name, arg_type):
+def gen_event_send(name, arg_type, box):
ret = mcgen('''
%(proto)s
@@ -34,7 +34,7 @@ def gen_event_send(name, arg_type):
Error *local_err = NULL;
QMPEventFuncEmit emit;
''',
- proto=gen_event_send_proto(name, arg_type))
+ proto=gen_event_send_proto(name, arg_type, box))
if arg_type and arg_type.members:
ret += mcgen('''
@@ -63,47 +63,52 @@ def gen_event_send(name, arg_type):
v = qmp_output_get_visitor(qov);
g_assert(v);
+''')
+
+ if box:
+ assert False # not implemented
+ else:
+ ret += mcgen('''
/* Fake visit, as if all members are under a structure */
visit_start_struct(v, NULL, "", "%(name)s", 0, &local_err);
if (local_err) {
goto clean;
}
-
''',
- name=name)
+ name=name)
- for memb in arg_type.members:
- if memb.optional:
- ret += mcgen('''
+ for memb in arg_type.members:
+ if memb.optional:
+ ret += mcgen('''
if (has_%(c_name)s) {
''',
- c_name=c_name(memb.name))
- push_indent()
+ c_name=c_name(memb.name))
+ push_indent()
- # Ugly: need to cast away the const
- if memb.type.name == "str":
- cast = '(char **)'
- else:
- cast = ''
+ # Ugly: need to cast away the const
+ if memb.type.name == "str":
+ cast = '(char **)'
+ else:
+ cast = ''
- ret += mcgen('''
+ ret += mcgen('''
visit_type_%(c_type)s(v, %(cast)s&%(c_name)s, "%(name)s", &local_err);
if (local_err) {
goto clean;
}
''',
- cast=cast,
- c_name=c_name(memb.name),
- c_type=memb.type.c_name(),
- name=memb.name)
+ cast=cast,
+ c_name=c_name(memb.name),
+ c_type=memb.type.c_name(),
+ name=memb.name)
- if memb.optional:
- pop_indent()
- ret += mcgen('''
+ if memb.optional:
+ pop_indent()
+ ret += mcgen('''
}
''')
- ret += mcgen('''
+ ret += mcgen('''
visit_end_struct(v, &local_err);
if (local_err) {
@@ -147,9 +152,9 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
self.decl += gen_enum(event_enum_name, self.event_names)
self.defn += gen_enum_lookup(event_enum_name, self.event_names)
self.event_names = None
- def visit_event(self, name, info, arg_type):
- self.decl += gen_event_send_decl(name, arg_type)
- self.defn += gen_event_send(name, arg_type)
+ def visit_event(self, name, info, arg_type, box):
+ self.decl += gen_event_send_decl(name, arg_type, box)
+ self.defn += gen_event_send(name, arg_type, box)
self.event_names.append(name)
(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index d723ef1..935ffc3 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -145,14 +145,14 @@ const char %(c_name)s[] = %(c_string)s;
for m in variants.variants] })
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, box):
arg_type = arg_type or self.schema.the_empty_object_type
ret_type = ret_type or self.schema.the_empty_object_type
self._gen_json(name, 'command',
{ 'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type) })
- def visit_event(self, name, info, arg_type):
+ def visit_event(self, name, info, arg_type, box):
arg_type = arg_type or self.schema.the_empty_object_type
self._gen_json(name, 'event', { 'arg-type': self._use_type(arg_type) })
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 61e4c00..313eaef 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -792,9 +792,9 @@ class QAPISchemaVisitor(object):
def visit_alternate_type(self, name, info, variants):
pass
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, box):
pass
- def visit_event(self, name, info, arg_type):
+ def visit_event(self, name, info, arg_type, box):
pass
class QAPISchemaType(QAPISchemaEntity):
@@ -986,7 +986,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
visitor.visit_alternate_type(self.name, self.info, self.variants)
class QAPISchemaCommand(QAPISchemaEntity):
- def __init__(self, name, info, arg_type, ret_type, gen, success_response):
+ def __init__(self, name, info, arg_type, ret_type, gen, success_response,
+ box):
QAPISchemaEntity.__init__(self, name, info)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
@@ -996,32 +997,36 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.ret_type = None
self.gen = gen
self.success_response = success_response
+ self.box = box
def check(self, schema):
if self.arg_type_name:
self.arg_type = schema.lookup_type(self.arg_type_name)
assert isinstance(self.arg_type, QAPISchemaObjectType)
- assert not self.arg_type.variants # not implemented
+ if not self.box:
+ assert not self.arg_type.variants
if self.ret_type_name:
self.ret_type = schema.lookup_type(self.ret_type_name)
assert isinstance(self.ret_type, QAPISchemaType)
def visit(self, visitor):
visitor.visit_command(self.name, self.info,
self.arg_type, self.ret_type,
- self.gen, self.success_response)
+ self.gen, self.success_response, self.box)
class QAPISchemaEvent(QAPISchemaEntity):
- def __init__(self, name, info, arg_type):
+ def __init__(self, name, info, arg_type, box):
QAPISchemaEntity.__init__(self, name, info)
assert not arg_type or isinstance(arg_type, str)
self.arg_type_name = arg_type
self.arg_type = None
+ self.box = box
def check(self, schema):
if self.arg_type_name:
self.arg_type = schema.lookup_type(self.arg_type_name)
assert isinstance(self.arg_type, QAPISchemaObjectType)
- assert not self.arg_type.variants # not implemented
+ if not self.box:
+ assert not self.arg_type.variants
def visit(self, visitor):
- visitor.visit_event(self.name, self.info, self.arg_type)
+ visitor.visit_event(self.name, self.info, self.arg_type, self.box)
class QAPISchema(object):
def __init__(self, fname):
@@ -1172,6 +1177,7 @@ class QAPISchema(object):
rets = expr.get('returns')
gen = expr.get('gen', True)
success_response = expr.get('success-response', True)
+ box = expr.get('box', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(name, 'arg',
self._make_members(data))
@@ -1182,15 +1188,16 @@ class QAPISchema(object):
rets = self._make_implicit_object_type(name, 'ret',
self._make_members(rets))
self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
- success_response))
+ success_response, box))
def _def_event(self, expr, info):
name = expr['event']
data = expr.get('data')
+ box = expr.get('box', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(name, 'arg',
self._make_members(data))
- self._def_entity(QAPISchemaEvent(name, info, data))
+ self._def_entity(QAPISchemaEvent(name, info, data, box))
def _def_exprs(self):
for expr_elem in self.exprs:
@@ -1413,18 +1420,22 @@ extern const char *const %(c_name)s_lookup[];
c_name=c_name(name))
return ret
-def gen_params(arg_type, extra):
+def gen_params(arg_type, box, extra):
if not arg_type:
return extra
- assert not arg_type.variants
ret = ''
sep = ''
- for memb in arg_type.members:
- ret += sep
- sep = ', '
- if memb.optional:
- ret += 'bool has_%s, ' % c_name(memb.name)
- ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
+ if box:
+ assert False # not implemented
+ else:
+ assert not arg_type.variants
+ for memb in arg_type.members:
+ ret += sep
+ sep = ', '
+ if memb.optional:
+ ret += 'bool has_%s, ' % c_name(memb.name)
+ ret += '%s %s' % (memb.type.c_type(is_param=True),
+ c_name(memb.name))
if extra:
ret += sep + extra
return ret
diff --git a/tests/qapi-schema/args-member-array.out
b/tests/qapi-schema/args-member-array.out
index b3b92df..f5dc409 100644
--- a/tests/qapi-schema/args-member-array.out
+++ b/tests/qapi-schema/args-member-array.out
@@ -6,4 +6,4 @@ enum abc ['a', 'b', 'c']
object def
member array: abcList optional=False
command okay :obj-okay-arg -> None
- gen=True success_response=True
+ gen=True success_response=True box=False
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index cdfd264..97a682a 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1,2 +1,3 @@
object :empty
event oops None
+ box=False
diff --git a/tests/qapi-schema/ident-with-escape.out
b/tests/qapi-schema/ident-with-escape.out
index f4542b1..2381c33 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -2,4 +2,4 @@ object :empty
object :obj-fooA-arg
member bar1: str optional=False
command fooA :obj-fooA-arg -> None
- gen=True success_response=True
+ gen=True success_response=True box=False
diff --git a/tests/qapi-schema/indented-expr.out
b/tests/qapi-schema/indented-expr.out
index 226d300..f5d2695 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,5 +1,5 @@
object :empty
command eins None -> None
- gen=True success_response=True
+ gen=True success_response=True box=False
command zwei None -> None
- gen=True success_response=True
+ gen=True success_response=True box=False
diff --git a/tests/qapi-schema/qapi-schema-test.out
b/tests/qapi-schema/qapi-schema-test.out
index ce01fa7..1a66627 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -70,9 +70,13 @@ alternate AltTwo
case s: str
case n: number
event EVENT_A None
+ box=False
event EVENT_B None
+ box=False
event EVENT_C :obj-EVENT_C-arg
+ box=False
event EVENT_D :obj-EVENT_D-arg
+ box=False
enum EnumOne ['value1', 'value2', 'value3']
object EventStructOne
member struct1: UserDefOne optional=False
@@ -148,6 +152,7 @@ object UserDefUnionBase
object UserDefZero
member integer: int optional=False
event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
+ box=False
alternate __org.qemu_x-Alt
case __org.qemu_x-branch: str
case b: __org.qemu_x-Base
@@ -167,14 +172,14 @@ object __org.qemu_x-Union2
tag __org.qemu_x-member1
case __org.qemu_x-value: __org.qemu_x-Struct2
command __org.qemu_x-command :obj-__org.qemu_x-command-arg ->
__org.qemu_x-Union1
- gen=True success_response=True
+ gen=True success_response=True box=False
command guest-sync :obj-guest-sync-arg -> any
- gen=True success_response=True
+ gen=True success_response=True box=False
command user_def_cmd None -> None
- gen=True success_response=True
+ gen=True success_response=True box=False
command user_def_cmd1 :obj-user_def_cmd1-arg -> None
- gen=True success_response=True
+ gen=True success_response=True box=False
command user_def_cmd2 :obj-user_def_cmd2-arg -> UserDefTwo
- gen=True success_response=True
+ gen=True success_response=True box=False
command user_def_cmd3 :obj-user_def_cmd3-arg -> int
- gen=True success_response=True
+ gen=True success_response=True box=False
diff --git a/tests/qapi-schema/returns-int.out
b/tests/qapi-schema/returns-int.out
index a2da259..911212d 100644
--- a/tests/qapi-schema/returns-int.out
+++ b/tests/qapi-schema/returns-int.out
@@ -1,3 +1,3 @@
object :empty
command guest-get-time None -> int
- gen=True success_response=True
+ gen=True success_response=True box=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 471f8e1..273cdc3 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -30,12 +30,14 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
print 'alternate %s' % name
self._print_variants(variants)
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, box):
print 'command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name)
- print ' gen=%s success_response=%s' % (gen, success_response)
- def visit_event(self, name, info, arg_type):
+ print ' gen=%s success_response=%s box=%s' % (gen, success_response,
+ box)
+ def visit_event(self, name, info, arg_type, box):
print 'event %s %s' % (name, arg_type and arg_type.name)
+ print ' box=%s' % box
@staticmethod
def _print_variants(variants):
--
2.4.3
- [Qemu-devel] [PATCH RFC v3 07/20] qapi: Fix alternates that accept 'number' but not 'int', (continued)
- [Qemu-devel] [PATCH RFC v3 07/20] qapi: Fix alternates that accept 'number' but not 'int', Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 09/20] qapi: Use consistent generated code patterns, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 08/20] qapi: Don't pass pre-existing error to later call, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 10/20] qapi: Add tests for empty unions, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 11/20] qapi: Rework deallocation of partial struct, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 04/20] qapi-visit: Remove redundant functions for flat union base, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 03/20] qapi: Unbox base members, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 02/20] vnc: hoist allocation of VncBasicInfo to callers, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 15/20] qapi: Remove dead visitor code, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 20/20] qapi: Support boxed unions, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 18/20] qapi: Plumb in 'box' to qapi generator lower levels,
Eric Blake <=
- [Qemu-devel] [PATCH RFC v3 16/20] qapi: Document visitor interfaces, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 13/20] qapi: Forbid empty unions and useless alternates, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 14/20] qapi: Drop useless 'data' member of unions, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 19/20] qapi: Implement boxed structs for commands/events, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 17/20] qapi: Change visit_type_XXX() to no longer return partial objects, Eric Blake, 2015/08/18
- [Qemu-devel] [PATCH RFC v3 12/20] qapi: Avoid use of 'data' member of qapi unions, Eric Blake, 2015/08/18