qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]