qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH] qom: add style guide


From: Jens Freimann
Subject: Re: [Qemu-devel] [PATCH] qom: add style guide
Date: Tue, 14 Aug 2012 09:14:42 +0200
User-agent: Mutt/1.5.21 (2010-09-15)

On Mon, Aug 13, 2012 at 01:46:46PM -0500, Anthony Liguori wrote:
> 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

"There code contains" -> "The code contains"?

> +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

"overriden _by_ subclasses"?

> +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

"we need to specify"?

Jens

> +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
> 
> 

-- 
Mit freundlichen Grüßen / Kind regards 

Jens Freimann 

-- 
IBM Linux Technology Center / Boeblingen lab
IBM Systems &Technology Group, Systems Software Development
-------------------------------------------------------------
IBM Deutschland
Schoenaicher Str 220
71032 Boeblingen
Phone: +49-7031-16 x1122
E-Mail: address@hidden
-------------------------------------------------------------
IBM Deutschland Research & Development GmbH
Vorsitzende des Aufsichtsrats: Martina Koederitz
Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen
Registergericht: Amtsgericht Stuttgart, HRB 243294




reply via email to

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