From: Kevin Wolf <kwolf@redhat.com>
To: qemu-devel@nongnu.org
Cc: kwolf@redhat.com, armbru@redhat.com, stefanha@redhat.com,
lcapitulino@redhat.com
Subject: [Qemu-devel] [PATCH 10/18] qapi: Anonymous unions
Date: Tue, 23 Jul 2013 15:03:18 +0200 [thread overview]
Message-ID: <1374584606-5615-11-git-send-email-kwolf@redhat.com> (raw)
In-Reply-To: <1374584606-5615-1-git-send-email-kwolf@redhat.com>
The discriminator for anonymous unions is the data type. This allows to
have a union type that allows both of these:
{ 'file': 'my_existing_block_device_id' }
{ 'file': { 'filename': '/tmp/mydisk.qcow2', 'read-only': true } }
Unions like this are specified in the schema with an empty dict as
discriminator. For this example you could take:
{ 'union': 'BlockRef',
'discriminator': {},
'data': { 'definition': 'BlockOptions'
'reference': 'str' } }
{ 'type': 'ExampleObject',
'data: { 'file': 'BlockRef' } }
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
docs/qapi-code-gen.txt | 25 ++++++++++++++++++++++++
include/qapi/qmp/qobject.h | 1 +
include/qapi/visitor-impl.h | 2 ++
include/qapi/visitor.h | 3 +++
qapi/qapi-visit-core.c | 9 +++++++++
qapi/qmp-input-visitor.c | 14 ++++++++++++++
qobject/qjson.c | 2 ++
scripts/qapi-types.py | 42 ++++++++++++++++++++++++++++++++++++++++
scripts/qapi-visit.py | 47 +++++++++++++++++++++++++++++++++++++++++++++
scripts/qapi.py | 15 +++++++++++++++
10 files changed, 160 insertions(+)
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index c187fda..ee8a13e 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -125,6 +125,31 @@ Resulting in this JSON object:
"lazy-refcounts": true }
+A special type of unions are anonymous unions. They don't form a dictionary in
+the wire format but allow the direct use of different types in their place. As
+they aren't structured, they don't have any explicit discriminator but use
+the (QObject) data type of their value as an implicit discriminator. This means
+that they are restricted to using only one discriminator value per QObject
+type. For example, you cannot have two different complex types in an anonymous
+union, or two different integer types.
+
+Anonymous unions are declared using an empty dictionary as their discriminator.
+The disriminator values never appear on the wire, they are only used in the
+generated C code. Anonymous unions cannot have a base type.
+
+ { 'union': 'BlockRef',
+ 'discriminator': {},
+ 'data': { 'definition': 'BlockdevOptions'
+ 'reference': 'str' } }
+
+This example allows using both of the following example objects:
+
+ { "file": "my_existing_block_device_id" }
+ { "file": { "driver": "file",
+ "readonly": false,
+ 'filename': "/tmp/mydisk.qcow2" } }
+
+
=== Commands ===
Commands are defined by using a list containing three members. The first
diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h
index 9124649..d0bbc7c 100644
--- a/include/qapi/qmp/qobject.h
+++ b/include/qapi/qmp/qobject.h
@@ -44,6 +44,7 @@ typedef enum {
QTYPE_QFLOAT,
QTYPE_QBOOL,
QTYPE_QERROR,
+ QTYPE_MAX,
} qtype_code;
struct QObject;
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 5c1297f..f3fa420 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -32,6 +32,8 @@ struct Visitor
void (*type_enum)(Visitor *v, int *obj, const char *strings[],
const char *kind, const char *name, Error **errp);
+ void (*get_next_type)(Visitor *v, int *kind, const int *qobjects,
+ const char *name, Error **errp);
void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index bd24f85..48a2a2e 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -13,6 +13,7 @@
#ifndef QAPI_VISITOR_CORE_H
#define QAPI_VISITOR_CORE_H
+#include "qapi/qmp/qobject.h"
#include "qapi/error.h"
#include <stdlib.h>
@@ -42,6 +43,8 @@ void visit_end_list(Visitor *v, Error **errp);
void visit_start_optional(Visitor *v, bool *present, const char *name,
Error **errp);
void visit_end_optional(Visitor *v, Error **errp);
+void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
+ const char *name, Error **errp);
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
const char *kind, const char *name, Error **errp);
void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 9b4d51b..d6a4012 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -12,6 +12,7 @@
*/
#include "qemu-common.h"
+#include "qapi/qmp/qobject.h"
#include "qapi/qmp/qerror.h"
#include "qapi/visitor.h"
#include "qapi/visitor-impl.h"
@@ -98,6 +99,14 @@ void visit_end_optional(Visitor *v, Error **errp)
}
}
+void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
+ const char *name, Error **errp)
+{
+ if (!error_is_set(errp) && v->get_next_type) {
+ v->get_next_type(v, obj, qtypes, name, errp);
+ }
+}
+
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
const char *kind, const char *name, Error **errp)
{
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 70864a1..bf42c04 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -208,6 +208,19 @@ static void qmp_input_end_list(Visitor *v, Error **errp)
qmp_input_pop(qiv, errp);
}
+static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects,
+ const char *name, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ QObject *qobj = qmp_input_get_object(qiv, name, false);
+
+ if (!qobj) {
+ error_set(errp, QERR_MISSING_PARAMETER, name ? name : "null");
+ return;
+ }
+ *kind = qobjects[qobject_type(qobj)];
+}
+
static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
Error **errp)
{
@@ -317,6 +330,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
v->visitor.type_str = qmp_input_type_str;
v->visitor.type_number = qmp_input_type_number;
v->visitor.start_optional = qmp_input_start_optional;
+ v->visitor.get_next_type = qmp_input_get_next_type;
qmp_input_push(v, obj, NULL);
qobject_incref(obj);
diff --git a/qobject/qjson.c b/qobject/qjson.c
index 19085a1..6cf2511 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -260,6 +260,8 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
/* XXX: should QError be emitted? */
case QTYPE_NONE:
break;
+ case QTYPE_MAX:
+ abort();
}
}
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index db2f533..57dff53 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -157,6 +157,40 @@ typedef enum %(name)s
return lookup_decl + enum_decl
+def generate_anon_union_qtypes(expr):
+
+ name = expr['union']
+ members = expr['data']
+
+ ret = mcgen('''
+const int %(name)s_qtypes[QTYPE_MAX] = {
+''',
+ name=name)
+
+ for key in members:
+ qapi_type = members[key]
+ if builtin_type_qtypes.has_key(qapi_type):
+ qtype = builtin_type_qtypes[qapi_type]
+ elif find_struct(qapi_type):
+ qtype = "QTYPE_QDICT"
+ elif find_union(qapi_type):
+ qtype = "QTYPE_QDICT"
+ else:
+ assert False, "Invalid anonymous union member"
+
+ ret += mcgen('''
+ [ %(qtype)s ] = %(abbrev)s_KIND_%(enum)s,
+''',
+ qtype = qtype,
+ abbrev = de_camel_case(name).upper(),
+ enum = c_fun(de_camel_case(key),False).upper())
+
+ ret += mcgen('''
+};
+''')
+ return ret
+
+
def generate_union(expr):
name = expr['union']
@@ -196,6 +230,12 @@ struct %(name)s
ret += mcgen('''
};
''')
+ if discriminator == {}:
+ ret += mcgen('''
+extern const int %(name)s_qtypes[];
+''',
+ name=name)
+
return ret
@@ -348,6 +388,8 @@ for expr in exprs:
ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
fdef.write(generate_enum_lookup('%sKind' % expr['union'], expr['data'].keys()))
+ if expr.get('discriminator') == {}:
+ fdef.write(generate_anon_union_qtypes(expr))
else:
continue
fdecl.write(ret)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index cd33e44..28539e2 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -172,6 +172,49 @@ void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **e
''',
name=name)
+def generate_visit_anon_union(name, members):
+ ret = mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
+{
+ Error *err = NULL;
+
+ if (!error_is_set(errp)) {
+ visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
+ visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
+ switch ((*obj)->kind) {
+''',
+ name=name)
+
+ for key in members:
+ assert (members[key] in builtin_types
+ or find_struct(members[key])
+ or find_union(members[key])), "Invalid anonymous union member"
+
+ ret += mcgen('''
+ case %(abbrev)s_KIND_%(enum)s:
+ visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
+ break;
+''',
+ abbrev = de_camel_case(name).upper(),
+ enum = c_fun(de_camel_case(key),False).upper(),
+ c_type = type_name(members[key]),
+ c_name = c_fun(key))
+
+ ret += mcgen('''
+ default:
+ abort();
+ }
+ error_propagate(errp, err);
+ err = NULL;
+ visit_end_implicit_struct(m, &err);
+ }
+}
+''')
+
+ return ret
+
+
def generate_visit_union(expr):
name = expr['union']
@@ -180,6 +223,10 @@ def generate_visit_union(expr):
base = expr.get('base')
discriminator = expr.get('discriminator')
+ if discriminator == {}:
+ assert not base
+ return generate_visit_anon_union(name, members)
+
ret = generate_visit_enum('%sKind' % name, members.keys())
if base:
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 3a54c7f..38c808e 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -17,6 +17,21 @@ builtin_types = [
'uint8', 'uint16', 'uint32', 'uint64'
]
+builtin_type_qtypes = {
+ 'str': 'QTYPE_QSTRING',
+ 'int': 'QTYPE_QINT',
+ 'number': 'QTYPE_QFLOAT',
+ 'bool': 'QTYPE_QBOOL',
+ 'int8': 'QTYPE_QINT',
+ 'int16': 'QTYPE_QINT',
+ 'int32': 'QTYPE_QINT',
+ 'int64': 'QTYPE_QINT',
+ 'uint8': 'QTYPE_QINT',
+ 'uint16': 'QTYPE_QINT',
+ 'uint32': 'QTYPE_QINT',
+ 'uint64': 'QTYPE_QINT',
+}
+
def tokenize(data):
while len(data):
ch = data[0]
--
1.8.1.4
next prev parent reply other threads:[~2013-07-23 13:04 UTC|newest]
Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-07-23 13:03 [Qemu-devel] [PATCH 00/18] 'blockdev-add' QMP command Kevin Wolf
2013-07-23 13:03 ` [Qemu-devel] [PATCH 01/18] qapi-types.py: Split off generate_struct_fields() Kevin Wolf
2013-07-25 23:06 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 02/18] qapi-types.py: Implement 'base' for unions Kevin Wolf
2013-07-25 23:12 ` Eric Blake
2013-08-21 3:38 ` Amos Kong
2013-08-27 15:58 ` Kevin Wolf
2013-08-29 13:52 ` Luiz Capitulino
2013-08-29 16:06 ` Kevin Wolf
2013-08-29 16:33 ` Luiz Capitulino
2013-08-29 16:50 ` Kevin Wolf
2013-08-29 17:02 ` Eric Blake
2013-08-29 18:36 ` Luiz Capitulino
2013-08-30 7:30 ` Wenchao Xia
2013-07-23 13:03 ` [Qemu-devel] [PATCH 03/18] qapi-visit.py: Split off generate_visit_struct_fields() Kevin Wolf
2013-07-25 23:15 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 04/18] qapi-visit.py: Implement 'base' for unions Kevin Wolf
2013-07-25 23:19 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 05/18] docs: Document QAPI union types Kevin Wolf
2013-07-26 13:06 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 06/18] qapi: Add visitor for implicit structs Kevin Wolf
2013-07-26 13:12 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 07/18] qapi: Flat unions with arbitrary discriminator Kevin Wolf
2013-07-26 13:40 ` Eric Blake
2013-07-26 15:01 ` Kevin Wolf
2013-07-26 15:13 ` Eric Blake
2013-07-26 19:16 ` [Qemu-devel] [PATCH v2 07/17] " Kevin Wolf
2013-07-26 19:36 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 08/18] qapi: Add consume argument to qmp_input_get_object() Kevin Wolf
2013-07-26 15:13 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 09/18] qapi.py: Maintain a list of union types Kevin Wolf
2013-07-26 15:26 ` Eric Blake
2013-07-23 13:03 ` Kevin Wolf [this message]
2013-07-26 15:42 ` [Qemu-devel] [PATCH 10/18] qapi: Anonymous unions Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 11/18] block: Allow "driver" option on the top level Kevin Wolf
2013-07-26 16:00 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 12/18] QemuOpts: Add qemu_opt_unset() Kevin Wolf
2013-07-26 16:04 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 13/18] blockdev: Rename I/O throttling options for QMP Kevin Wolf
2013-07-26 16:10 ` Eric Blake
2013-07-26 16:26 ` Kevin Wolf
2013-07-26 16:44 ` Eric Blake
2013-07-26 16:54 ` Kevin Wolf
2013-07-26 17:01 ` Eric Blake
2013-07-26 19:35 ` Benoît Canet
2013-07-26 19:54 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 14/18] qcow2: Use dashes instead of underscores in options Kevin Wolf
2013-07-26 16:17 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 15/18] blockdev: Rename 'readonly' option to 'read-only' Kevin Wolf
2013-07-26 16:20 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 16/18] blockdev: Split up 'cache' option Kevin Wolf
2013-07-26 16:30 ` Eric Blake
2013-07-23 13:03 ` [Qemu-devel] [PATCH 17/18] Implement qdict_flatten() Kevin Wolf
2013-07-26 16:40 ` Eric Blake
2013-07-26 17:03 ` Kevin Wolf
2013-07-23 13:03 ` [Qemu-devel] [PATCH 18/18] blockdev: 'blockdev-add' QMP command Kevin Wolf
2013-07-26 17:45 ` Eric Blake
2013-07-26 18:14 ` Kevin Wolf
2013-07-30 17:44 ` Luiz Capitulino
2013-07-31 8:09 ` Kevin Wolf
2013-08-30 7:41 ` Wenchao Xia
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=1374584606-5615-11-git-send-email-kwolf@redhat.com \
--to=kwolf@redhat.com \
--cc=armbru@redhat.com \
--cc=lcapitulino@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@redhat.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.