qapi: Anonymous unions
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> Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
ea66c6d881
commit
69dd62dfd6
@ -125,6 +125,31 @@ Resulting in this JSON object:
|
|||||||
"lazy-refcounts": true }
|
"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 discriminator 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 ===
|
||||||
|
|
||||||
Commands are defined by using a list containing three members. The first
|
Commands are defined by using a list containing three members. The first
|
||||||
|
@ -44,6 +44,7 @@ typedef enum {
|
|||||||
QTYPE_QFLOAT,
|
QTYPE_QFLOAT,
|
||||||
QTYPE_QBOOL,
|
QTYPE_QBOOL,
|
||||||
QTYPE_QERROR,
|
QTYPE_QERROR,
|
||||||
|
QTYPE_MAX,
|
||||||
} qtype_code;
|
} qtype_code;
|
||||||
|
|
||||||
struct QObject;
|
struct QObject;
|
||||||
|
@ -32,6 +32,8 @@ struct Visitor
|
|||||||
|
|
||||||
void (*type_enum)(Visitor *v, int *obj, const char *strings[],
|
void (*type_enum)(Visitor *v, int *obj, const char *strings[],
|
||||||
const char *kind, const char *name, Error **errp);
|
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_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
|
||||||
void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
|
void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#ifndef QAPI_VISITOR_CORE_H
|
#ifndef QAPI_VISITOR_CORE_H
|
||||||
#define QAPI_VISITOR_CORE_H
|
#define QAPI_VISITOR_CORE_H
|
||||||
|
|
||||||
|
#include "qapi/qmp/qobject.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include <stdlib.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,
|
void visit_start_optional(Visitor *v, bool *present, const char *name,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void visit_end_optional(Visitor *v, 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[],
|
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||||
const char *kind, const char *name, Error **errp);
|
const char *kind, const char *name, Error **errp);
|
||||||
void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
|
void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qapi/qmp/qobject.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
#include "qapi/visitor-impl.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[],
|
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||||
const char *kind, const char *name, Error **errp)
|
const char *kind, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -208,6 +208,19 @@ static void qmp_input_end_list(Visitor *v, Error **errp)
|
|||||||
qmp_input_pop(qiv, 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,
|
static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@ -317,6 +330,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
|
|||||||
v->visitor.type_str = qmp_input_type_str;
|
v->visitor.type_str = qmp_input_type_str;
|
||||||
v->visitor.type_number = qmp_input_type_number;
|
v->visitor.type_number = qmp_input_type_number;
|
||||||
v->visitor.start_optional = qmp_input_start_optional;
|
v->visitor.start_optional = qmp_input_start_optional;
|
||||||
|
v->visitor.get_next_type = qmp_input_get_next_type;
|
||||||
|
|
||||||
qmp_input_push(v, obj, NULL);
|
qmp_input_push(v, obj, NULL);
|
||||||
qobject_incref(obj);
|
qobject_incref(obj);
|
||||||
|
@ -260,6 +260,8 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
|
|||||||
/* XXX: should QError be emitted? */
|
/* XXX: should QError be emitted? */
|
||||||
case QTYPE_NONE:
|
case QTYPE_NONE:
|
||||||
break;
|
break;
|
||||||
|
case QTYPE_MAX:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +150,40 @@ typedef enum %(name)s
|
|||||||
|
|
||||||
return lookup_decl + enum_decl
|
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):
|
def generate_union(expr):
|
||||||
|
|
||||||
name = expr['union']
|
name = expr['union']
|
||||||
@ -190,6 +224,12 @@ struct %(name)s
|
|||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
};
|
};
|
||||||
''')
|
''')
|
||||||
|
if discriminator == {}:
|
||||||
|
ret += mcgen('''
|
||||||
|
extern const int %(name)s_qtypes[];
|
||||||
|
''',
|
||||||
|
name=name)
|
||||||
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@ -342,6 +382,8 @@ for expr in exprs:
|
|||||||
ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
|
ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
|
||||||
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
||||||
fdef.write(generate_enum_lookup('%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:
|
else:
|
||||||
continue
|
continue
|
||||||
fdecl.write(ret)
|
fdecl.write(ret)
|
||||||
|
@ -176,6 +176,49 @@ void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **e
|
|||||||
''',
|
''',
|
||||||
name=name)
|
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):
|
def generate_visit_union(expr):
|
||||||
|
|
||||||
name = expr['union']
|
name = expr['union']
|
||||||
@ -184,6 +227,10 @@ def generate_visit_union(expr):
|
|||||||
base = expr.get('base')
|
base = expr.get('base')
|
||||||
discriminator = expr.get('discriminator')
|
discriminator = expr.get('discriminator')
|
||||||
|
|
||||||
|
if discriminator == {}:
|
||||||
|
assert not base
|
||||||
|
return generate_visit_anon_union(name, members)
|
||||||
|
|
||||||
ret = generate_visit_enum('%sKind' % name, members.keys())
|
ret = generate_visit_enum('%sKind' % name, members.keys())
|
||||||
|
|
||||||
if base:
|
if base:
|
||||||
|
@ -17,6 +17,21 @@ builtin_types = [
|
|||||||
'uint8', 'uint16', 'uint32', 'uint64'
|
'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):
|
def tokenize(data):
|
||||||
while len(data):
|
while len(data):
|
||||||
ch = data[0]
|
ch = data[0]
|
||||||
|
Loading…
Reference in New Issue
Block a user