[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] qom: add style guide
From: |
Anthony Liguori |
Subject: |
[Qemu-devel] [PATCH] qom: add style guide |
Date: |
Mon, 13 Aug 2012 13:46:46 -0500 |
Signed-off-by: Anthony Liguori <address@hidden>
---
docs/qom-style-guide.md | 489 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 489 insertions(+), 0 deletions(-)
create mode 100644 docs/qom-style-guide.md
diff --git a/docs/qom-style-guide.md b/docs/qom-style-guide.md
new file mode 100644
index 0000000..e7590e0
--- /dev/null
+++ b/docs/qom-style-guide.md
@@ -0,0 +1,489 @@
+QEMU Object Model Style Guide
+=============================
+
+Overview
+--------
+This document is a step-by-step tutorial of QOM. It is meant to be read from
+top to bottom addressing the most common use-cases at the start. This is a
+living document and contributions are welcome but it should not attempt to be
+an API reference. There code contains inline documentation and API details
+should be covered in the respective header files.
+
+Motivation
+----------
+QEMU makes extensive use of object oriented programming. Since QEMU is written
+in C, these OOP concepts often use very different mechanisms to achieve the
+same goal. The net effect is a lot of infrastructure duplication and a general
+lack of consistency.
+
+The goal of QOM is to use a single infrastructure for all OOP within QEMU.
This
+improves consistency and eases maintainability over the long term.
+
+QOM provides a common infrastructure for:
+
+- Type Management
+ - Registering types
+ - Enumerating registered types
+- Inheritence
+ - Single-parent inheritance
+ - Introspection of inheritance hierarchy
+ - Multiple inheritance through stateless interfaces
+- Polymorphism
+ - Class based polymorphism
+ - Virtual and pure virtual methods
+ - Constructor/destructor chaining
+- Object Properties
+ - Dynamic property registration (tied to Objects)
+ - Property introspection
+ - Access permissions
+ - Accessor hooks
+- Type Casting
+ - Runtime checked upcast/downcast
+ - Full support for casting up and down the chain (including interfaces)
+- Object Enumeration
+ - Expression of relationship between objects
+ - Ability to reference objects with a symbolic path
+ - Represented as a directed graph
+
+While QOM has a lot of high level concepts, the primary design goal has been to
+keep simple concepts simple to implement.
+
+A Note on Consistency
+---------------------
+Much of the QOM types in QEMU have been converted through automated scripts.
+Tremendous effort was made to make sure the resulting code was high quality and
+adhered to all of the guidelines in this document. However, there are many
+cases where some of the conversion could not be scripted easily and some short
+cuts where taken.
+
+Whenever possible, as code is refactored for other reasons, it should be
brought
+up fully to the guidelines expressed in this document.
+
+Creating a Simple Type
+----------------------
+The easiest way to understand QOM is to walk through an example. This is a
+typical example of creating a new type derived from Object as the parent class.
+In this example, all code would live in a single C source file.
+
+Let's get started:
+
+ #include "qemu/object.h"
+
+This is the header file that contains the core QOM infrastructure. It has
+minimum dependencies to facilitate unit testing.
+
+ #define TYPE_MY_TYPE "my-type"
+ #define MY_TYPE(obj) OBJECT_CHECK(MyType, (obj), TYPE_MY_TYPE)
+
+All QOM types should define at least two macros. The first macro is a symbolic
+version of the type name. It should always take the form
+TYPE_ + upper(typename). Type names should generally follow the naming rules
of
+QAPI which means dashes, '-', are preferred to underscores, '_'.
+
+The second macro is a cast macro. The first argument is the type struct and
the
+remaining arguments are self-evident. This form should always be followed even
+if the cast macro isn't currently used by the C file.
+
+ typedef struct MyType MyType;
+
+ struct MyType
+ {
+ Object parent_obj;
+
+ /*< private >*/
+ int foo;
+ };
+
+When declaring the structure, a forward declaration should be used. This is
+useful for consistency sake as it is required when defining classes.
+
+The first element must be the parent type and should be named 'parent_obj' or
+just 'parent'. When working with QOM types, you should avoid ever accessing
+this member directly instead relying on casting macros.
+
+Casting macros hide the inheritence hierarchy from the implementation. This
+makes it easier to refactor code over time by changing the hierarchy without
+changing the code in many places.
+
+ static TypeInfo my_type_info = {
+ .name = TYPE_MY_TYPE,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(MyType),
+ };
+
+ static void register_types(void)
+ {
+ type_register_static(&my_type_info);
+ }
+
+ type_init(register_types);
+
+All QOM types must be registered with the QOM infrastructure. Once registered,
+the user has the ability to enumerate types, create objects, and interact with
+objects without any additional code.
+
+All types must set the 'name' and 'parent' parameters. The type macros should
+always be used for these parameters. Almost all types should set the
+'instance_size' parameter although if it's not specified, it will be inherited
+from its parent.
+
+Finally, a module init function should be provided. The naming convention
+shown here should be used in all new code.
+
+In general, one C file should register one type. There are many valid
+exceptions to this rule but whenever possible, types should be split out into
+separate C files.
+
+Creating a Type with Methods
+----------------------------
+The next most common interaction with QOM will be to create a type that will be
+inherited from another type. This usually involves adding a class and
+implementing a virtual method that can be overridden subclasses. The
+following diff shows the changes we would need to extend our previous example
to
+allow inheritance with polymorphism.
+
+ @@ -1,10 +1,25 @@
+ +#ifndef QEMU_MY_TYPE_H
+ +#define QEMU_MY_TYPE_H
+ +
+ #include "qemu/object.h"
+
+This example assumes that the initial declarations will be split into a
+separate header. To simplify the example, guards are used to show where the
+header file starts and ends.
+
+ #define TYPE_MY_TYPE "my-type"
+ #define MY_TYPE(obj) \
+ OBJECT_CHECK(MyType, (obj), TYPE_MY_TYPE)
+ +#define MY_TYPE_CLASS(klass) \
+ + OBJECT_CLASS_CHECK(MyTypeClass, (klass), TYPE_MY_TYPE)
+ +#define MY_TYPE_GET_CLASS(obj) \
+ + OBJECT_GET_CLASS(MyTypeClass, (obj), TYPE_MY_TYPE)
+
+When adding a class, we need to add two more macros to the type definition.
The
+first macro is a class casting macro. This looks very similar to an object
cast
+macro but instead takes a class as an argument.
+
+The second macro that we add allows a user to get a class pointer from an
+object. Method dispatch requires this last macro.
+
+ typedef struct MyType MyType;
+ +typedef struct MyTypeClass MyTypeClass;
+ +
+ +struct MyTypeClass
+ +{
+ + ObjectClass parent_klass;
+ +
+ + void (*bar)(MyType *obj, int foo);
+ +};
+
+A class looks very similar to an object in that it is expressed as a C
structure
+and the first member must be the class of the parent type.
+
+Typically classes will only contain function pointers but it is possible to
have
+data members of a class. The first argument to each function pointer should
+always be the object type.
+
+ struct MyType
+ {
+ @@ -14,10 +29,35 @@ struct MyType
+ int foo;
+ };
+
+ +void my_type_bar(MyType *obj, int foo);
+ +
+ +#endif
+
+A helper function should be provided for doing method dispatch. This improves
+readability and convenience.
+
+ +
+ +static void my_type_default_bar(MyType *obj, int foo)
+ +{
+ + /* do nothing */
+ +}
+ +
+ +void my_type_bar(MyType *obj, int foo)
+ +{
+ + MyTypeClass *mc = MY_TYPE_GET_CLASS(obj);
+ +
+ + mc->bar(obj, foo);
+ +}
+ +
+ +static void my_type_class_init(ObjectClass *klass, void *data)
+ +{
+ + MyTypeClass *mc = MY_TYPE_CLASS(klass);
+ +
+ + mc->bar = my_type_default_bar;
+ +}
+ +
+ static TypeInfo my_type_info = {
+ .name = TYPE_MY_TYPE,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(MyType),
+ + .class_size = sizeof(MyTypeClass),
+ + .class_init = my_type_class_init,
+ };
+
+ static void register_types(void)
+
+In order to add a new class for an type, we need to specific the size of the
+class in the TypeInfo. We also need to provide a function to initialize the
+class. Classes are only ever created and initialized once for any type so this
+function will be called once regardless of how many objects are created of this
+type.
+
+The class init function should follow a naming convention of
+typename + '_class_init'. The class init function should cast the klass
+parameter to the appropriate type and then overload the methods appropriately.
+
+In this example, we're initializing the method to a dummy function that does
+nothing useful. This is because 'foo' is a virtual method meaning that base
+classes do not need to implement the function if they don't want to override
+the behavior.
+
+If we did not initialize the method, the function would be a pure virtual
method
+meaning that the subclass must implement the function. QOM cannot enforce this
+requirement so care should be taken in the wrapper function to check for NULL.
+
+The wrapper function simply dispatches the method. It should not implement any
+logic or behavior beyond just dispatching the method. Checking for NULL and
+either returning an error or asserting is acceptable behavior for a wrapper
+function.
+
+Implementing Devices and Overloading Methods
+--------------------------------------------
+Most QOM users will not implement objects that derive from TYPE_OBJECT.
+Instead, usually QOM users will derive from TYPE_DEVICE or some other base
+class and will also have to implement virtual methods.
+
+In this example, we change MyType to inherit from TYPE_DEVICE and then
implement
+the required pure virtual method.
+
+ @@ -16,14 +16,14 @@ typedef struct MyTypeClass MyTypeClass;
+
+ struct MyTypeClass
+ {
+ - ObjectClass parent_klass;
+ + DeviceClass parent_klass;
+
+ void (*bar)(MyType *obj, int foo);
+ };
+
+ struct MyType
+ {
+ - Object parent_obj;
+ + DeviceState parent_obj;
+
+ /*< private >*/
+ int foo;
+
+Changing the parent type is trivial as it just requires modifying the
+structures. This is one of the benefits of doing all casts through the cast
+macro. It simplifies the process of refactoring.
+
+ @@ -45,16 +45,27 @@ void my_type_bar(MyType *obj, int foo)
+ mc->bar(obj, foo);
+ }
+
+ +static int my_type_realize(DeviceState *dev)
+ +{
+ + MyType *my = MY_TYPE(dev);
+ +
+ + my->foo = 1;
+ +
+ + return 0;
+ +}
+ +
+ static void my_type_class_init(ObjectClass *klass, void *data)
+ {
+ MyTypeClass *mc = MY_TYPE_CLASS(klass);
+ + DeviceClass *dc = DEVICE_CLASS(klass);
+
+ mc->bar = my_type_default_bar;
+ + dc->init = my_type_realize;
+ }
+
+TYPE_DEVICE has a pure virtual method 'init' which is a bit of a misnomer. The
+'init' method is called after construction but before the guest is started for
+the first time. In QOM nomenclature, we call this realize. At some point in
+time, TYPE_DEVICE will be refactored to rename the init method to realize but
+for now, we have to live with this inconsistency.
+
+ static TypeInfo my_type_info = {
+ .name = TYPE_MY_TYPE,
+ - .parent = TYPE_OBJECT,
+ + .parent = TYPE_DEVICE,
+ .instance_size = sizeof(MyType),
+ .class_size = sizeof(MyTypeClass),
+ .class_init = my_type_class_init,
+
+Using Instance Initialization
+-----------------------------
+
+QDev required all initialization and destruction to happen through 'init' and
+'exit' methods. Since QDev didn't have a concept of constructors and
+destructors, it was up to the type implementors to implement chaining which
+often was done in an inconsistent fashion.
+
+As part of the TypeInfo structure, QOM has a instance_init and
instance_finalize
+method which acts as the constructor and destructor respectively. These
+functions are called starting with the subclass and working up the type
+hierarchy by QOM.
+
+Any state that can be initialized independently of user supplied state should
be
+initialized as part of the constructor.
+
+ @@ -33,6 +33,13 @@ void my_type_bar(MyType *obj, int foo);
+
+ #endif
+
+ +static void my_type_initfn(Object *obj)
+ +{
+ + MyType *my = MY_TYPE(obj);
+ +
+ + my->foo = 1;
+ +}
+ +
+ static void my_type_default_bar(MyType *obj, int foo)
+ {
+ /* do nothing */
+ @@ -47,10 +54,6 @@ void my_type_bar(MyType *obj, int foo)
+
+ static int my_type_realize(DeviceState *dev)
+ {
+ - MyType *my = MY_TYPE(dev);
+ -
+ - my->foo = 1;
+ -
+ return 0;
+ }
+
+ @@ -69,6 +72,7 @@ static TypeInfo my_type_info = {
+ .instance_size = sizeof(MyType),
+ .class_size = sizeof(MyTypeClass),
+ .class_init = my_type_class_init,
+ + .instance_init = my_type_initfn,
+ };
+
+ static void register_types(void)
+
+Since 'foo' can be initialized without relying on user provided state, we can
+move that logic entirely to the constructor. Unfortunately, the DeviceState
+init function must remain since it is pure virtual but it is now trivial.
+
+User Provided State (Properties)
+--------------------------------
+
+A common property of most objects within QEMU is that there is a desire to
+allow users to adjust parameters of the object either during initial creation
or
+at run time. Properties provide a common framework for doing this.
+
+Properties are rich and complex and will not be covered exhaustively here.
+Refer to the documentation in the qemu/object.h header file for exhaustive
+documentation.
+
+Most interactions with properties will happen through convenience functions
+that make adding properties easier for typical users. In the case of our
+example, we'll add properties using the qdev static property interface.
+
+ @@ -27,6 +27,7 @@ struct MyType
+
+ /*< private >*/
+ int foo;
+ + int max_vectors;
+ };
+
+With static properties, a specific property corresponds to a member of the
+object structure. The infrastructure in qdev may change this member's value
+automatically at any time before calling DeviceState::init(). That means any
+initialization that depends on members exposed as properties must be done in
+the DeviceState::init method.
+
+ void my_type_bar(MyType *obj, int foo);
+ @@ -54,9 +55,20 @@ void my_type_bar(MyType *obj, int foo)
+
+ static int my_type_realize(DeviceState *dev)
+ {
+ + MyType *mt = MY_TYPE(dev);
+ +
+ + if (mt->max_vectors > 100) {
+ + return -EINVAL;
+ + }
+ +
+ return 0;
+ }
+
+For this example, we simply validate the property contains a sane value and
fail
+realize.
+
+ +static Property my_type_properties[] = {
+ + DEFINE_PROP_INT("max-vectors", MyType, max_vectors, 0),
+ + DEFINE_PROP_END_OF_LIST(),
+ +};
+ +
+ static void my_type_class_init(ObjectClass *klass, void *data)
+ {
+ MyTypeClass *mc = MY_TYPE_CLASS(klass);
+ @@ -64,6 +76,7 @@ static void my_type_class_init(ObjectClass *klass,
+
+ mc->bar = my_type_default_bar;
+ dc->init = my_type_realize;
+ + dc->props = my_type_properties;
+ }
+
+ static TypeInfo my_type_info = {
+
+Static properties are registered using a static class variable in the
+TYPE_DEVICE class. Base classes can add static properties using this approach
+and subclasses will automatically inherit them.
+
+Child and Link Properties
+-------------------------
+
+The other common types of properties in QOM are child and link properties.
Like
+static properties, there are special helpers to add these properties to an
+object.
+
+ @@ -25,6 +25,9 @@ struct MyType
+ {
+ DeviceState parent_obj;
+
+ + Pin *in;
+ + Pin out;
+ +
+ /*< private >*/
+ int foo;
+ int max_vectors;
+
+To begin with, we have to add struct members in that object that will hold the
+properties. A link is a pointer to another object and is represented as a
+pointer in C. A child property is an embedded object and is represented by
+embedding a struct member in the object structure.
+
+A child property has its lifecycle tied to the parent object. IOW, when an
+object of MyType is destroyed, the 'out' object embedded within it will be
+automatically destroyed.
+
+A link property will hold a reference to the object it points to, but does not
+control the life cycle of the object it points to. That is, when an object of
+MyType is destroyed, the object pointed to by 'in' does not necessarily get
+destroyed although its reference count will be decreased.
+
+ @@ -39,6 +42,11 @@ static void my_type_initfn(Object *obj)
+ MyType *my = MY_TYPE(obj);
+
+ my->foo = 1;
+ +
+ + object_initialize(&my->out, TYPE_PIN);
+ + object_property_add_child(obj, "out", OBJECT(&my->out), NULL);
+ +
+ + object_property_add_link(obj, "in", TYPE_PIN,
+ + (Object **)&my->in, NULL);
+ }
+
+ static void my_type_default_bar(MyType *obj, int foo)
+
+To add the properties to our object, we need to first initialize our child
+object and then add the properties. This should always be done in the
+constructor whenever possible.
--
1.7.5.4
- [Qemu-devel] [PATCH] qom: add style guide,
Anthony Liguori <=