Jump to content

Policies/Library Code Policy: Difference between revisions

From KDE Community Wiki
Dhaumann (talk | contribs)
m svn r610179: remove wrong statement.
Dhaumann (talk | contribs)
m add <code cppqt> wrappers. code -> tt
Line 13: Line 13:


Acronyms are lowercased too. Example:
Acronyms are lowercased too. Example:
<code>KUrl</code> instead of <code>KURL</code> and <code>isNssEnabled()</code> instead of <code>isNSSEnabled()</code>
<tt>KUrl</tt> instead of <tt>KURL</tt> and <tt>isNssEnabled()</tt> instead of <tt>isNSSEnabled()</tt>


Accessors should usually be <code>const</code>.
Accessors should usually be <tt>const</tt>.


This example shows some possible functions names
This example shows some possible functions names
public:
<code cppqt>
    void setColor(const QColor& c);
public:
    QColor color() const;
    void setColor(const QColor& c);
    void setDirty(bool b);
    QColor color() const;
    bool isDirty() const;
    void setDirty(bool b);
    bool isDirty() const;
private Q_SLOTS:
 
    void slotParentChanged();
private Q_SLOTS:
Make one public class for every .h file. Add the <code>_EXPORT</code> macro related to the library they are in.
    void slotParentChanged();
</code>
Make one public class for every .h file. Add the <tt>_EXPORT</tt> macro related to the library they are in.
Private classes should be declared in the .cpp file, or in a _p.h file.
Private classes should be declared in the .cpp file, or in a _p.h file.


Line 32: Line 34:
In order to more easily maintain binary compatibility, there shouldn't be private members in a public class. For more information about binary compatibility, read [http://developer.kde.org/documentation/other/binarycompatibility.html|Binary Compatibility Issues With C++].
In order to more easily maintain binary compatibility, there shouldn't be private members in a public class. For more information about binary compatibility, read [http://developer.kde.org/documentation/other/binarycompatibility.html|Binary Compatibility Issues With C++].


By convention, the private class will be named the same as the public class, with <code>Private</code> appended to the name.
By convention, the private class will be named the same as the public class, with <tt>Private</tt> appended to the name.
class KFooPrivate;
<code cppqt>
class KFoo
class KFooPrivate;
{
class KFoo
    public:
{
        /* public members */
    public:
    private:
        /* public members */
        KFooPrivate * const d;
    private:
};
        KFooPrivate * const d;
};
</code>
In the .cpp file:
<code>
class KFooPrivate
{
    public:
        int someInteger;
};
 
KFoo::KFoo() : d(new KFooPrivate)
{
    /* ... */
}


In the .cpp file:
KFoo::~KFoo()
class KFooPrivate
{
{
    delete d;
    public:
}
        int someInteger;
</code>
};
Notice that the member d is <tt>const</tt> to avoid modifying it by mistake.
KFoo::KFoo() : d(new KFooPrivate)
{
    /* ... */
}
KFoo::~KFoo()
{
    delete d;
}
Notice that the member d is <code>const</code> to avoid modifying it by mistake.


If you are implementing an implicitly shared class, you should consider using {{qt|QSharedData}} and {{qt|QSharedDataPointer}} for d. If you don't use them, then use QAtomic for reference counting. Don't try to implement your own refcounting with integers.
If you are implementing an implicitly shared class, you should consider using {{qt|QSharedData}} and {{qt|QSharedDataPointer}} for d. If you don't use them, then use QAtomic for reference counting. Don't try to implement your own refcounting with integers.
Line 68: Line 73:


If ever you add inline code please note the following:
If ever you add inline code please note the following:
* Installed headers should compile with the following preprocessor defines: <code>QT_NO_CAST_FROM_ASCII</code>, <code>QT_NO_CAST_TO_ASCII</code>, <code>QT_NO_KEYWORD</code>. So don't forget {{qt|QLatin1String}}.
* Installed headers should compile with the following preprocessor defines: <tt>QT_NO_CAST_FROM_ASCII</tt>, <tt>QT_NO_CAST_TO_ASCII</tt>, <tt>QT_NO_KEYWORD</tt>. So don't forget {{qt|QLatin1String}}.
* No C casts in the header. Use <code>static_cast</code> if types are known. Use <code>qobject_cast</code> instead of <code>dynamic_cast</code> if types are QObject based. dynamic_cast is not only slower, but is also unreliable across shared libraries.
* No C casts in the header. Use <tt>static_cast</tt> if types are known. Use <tt>qobject_cast</tt> instead of <tt>dynamic_cast</tt> if types are QObject based. dynamic_cast is not only slower, but is also unreliable across shared libraries.
* In general, check your code for [http://developer.kde.org/documentation/other/mistakes.html common mistakes].
* In general, check your code for [http://developer.kde.org/documentation/other/mistakes.html common mistakes].


Line 82: Line 87:
  window->setCaption(KApplication::makeStdCaption("Document Foo", true, true));
  window->setCaption(KApplication::makeStdCaption("Document Foo", true, true));


The solution is to use {{qt|QFlags}}. If the options only apply to one function, call the <code>enum FunctionNameOption</code> and the QFlags typedef <code>FunctionNameOptions</code>. Do that even if there is only one option, this will allow you to add more options later and keep the binary compatibility.
The solution is to use {{qt|QFlags}}. If the options only apply to one function, call the <tt>enum FunctionNameOption</tt> and the QFlags typedef <tt>FunctionNameOptions</tt>. Do that even if there is only one option, this will allow you to add more options later and keep the binary compatibility.


So a better API would be:
So a better API would be:
class KApplication
<code cppqt>
{
class KApplication
    public:
{
        /* [...] */
    public:
        enum MakeStandardCaptionOption {
        /* [...] */
            /**
        enum MakeStandardCaptionOption {
              * Indicates that the method shall include the application name
            /**
              */
            * Indicates that the method shall include the application name
            WithApplicationName = 0x01,
            */
            /**
            WithApplicationName = 0x01,
              * If set, a 'modified' sign is included in the returned string.
            /**
              */
            * If set, a 'modified' sign is included in the returned string.
            Modified = 0x02
            */
        };
            Modified = 0x02
        Q_DECLARE_FLAGS(MakeStandardCaptionOptions, MakeStandardCaptionOption)
        };
        Q_DECLARE_FLAGS(MakeStandardCaptionOptions, MakeStandardCaptionOption)
        /**
 
          * Builds a caption using a standard layout.
        /**
          *
        * Builds a caption using a standard layout.
          * @param userCaption The caption string you want to display
        *
          * @param options a set of flags from MakeStandartCaptionOption
        * @param userCaption The caption string you want to display
          */
        * @param options a set of flags from MakeStandartCaptionOption
        static QString makeStandardCaption(const QString& userCaption,
        */
            const MakeStandardCaptionOptions& options = WithApplicationName);
        static QString makeStandardCaption(const QString& userCaption,
        /* [...] */
            const MakeStandardCaptionOptions& options = WithApplicationName);
};
        /* [...] */
Q_DECLARE_OPERATORS_FOR_FLAGS(KApplication::MakeStandardCaptionOptions)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KApplication::MakeStandardCaptionOptions)
</code>


== Const References ==
== Const References ==
Line 118: Line 125:


== Signals and Slots ==
== Signals and Slots ==
In the libraries, use <code>Q_SIGNALS</code> and <code>Q_SLOTS</code> instead of <code>signals</code> and <code>slots</code>. They are syntactically equivalent and should be used to avoid conflicts with boost signals, and with python's use of "slots" in its headers.
In the libraries, use <tt>Q_SIGNALS</tt> and <tt>Q_SLOTS</tt> instead of <tt>signals</tt> and <tt>slots</tt>. They are syntactically equivalent and should be used to avoid conflicts with boost signals, and with python's use of "slots" in its headers.


== Properites ==
== Properites ==
Consider using <code>Q_PROPERTY</code> for properties. The reason is that properties (especially thoses marked <code>SCRIPTABLE</code>) will be accessible through the javascript interface.
Consider using <tt>Q_PROPERTY</tt> for properties. The reason is that properties (especially thoses marked <tt>SCRIPTABLE</tt>) will be accessible through the javascript interface.


If you follow the propname / setPropname naming sheme, moc sets a special flag for the {{qt|QMetaProperty}}.
If you follow the propname / setPropname naming sheme, moc sets a special flag for the {{qt|QMetaProperty}}.


== Explicit Constructors ==
== Explicit Constructors ==
For each constructor (other than the copy constructor), check if you should make the constructor <code>explicit</code> in order to minimize wrong use of the constructor.
For each constructor (other than the copy constructor), check if you should make the constructor <tt>explicit</tt> in order to minimize wrong use of the constructor.


Basically, each constructor that may take only one argument should be marked <code>explicit</code> unless the whole point of the constructor is to allow implicit casting.
Basically, each constructor that may take only one argument should be marked <tt>explicit</tt> unless the whole point of the constructor is to allow implicit casting.


== Avoid including other headers in headers ==
== Avoid including other headers in headers ==
Line 136: Line 143:


In this example, the class KFoo uses KBar by reference, so we do not need to include KBar's header:
In this example, the class KFoo uses KBar by reference, so we do not need to include KBar's header:
#include <kfoobase.h>
<code cppqt>
class KBar;
#include <kfoobase.h>
class KFoo : public KFooBase
class KBar;
{
class KFoo : public KFooBase
    public:
{
        /* [...] */            
    public:
        void myMethod(const KBar& bar);
        /* [...] */
};
        void myMethod(const KBar& bar);
};
</code>


== Static Objects ==
== Static Objects ==
Global static objects in libraries should be avoided. You never know when the constructor will be run or if it will be run at all.
Global static objects in libraries should be avoided. You never know when the constructor will be run or if it will be run at all.
; Wrong
; Wrong
static QString foo; // wrong - object might not be constructed
<code cppqt>
static QString bar("hello"); // as above
static QString foo; // wrong - object might not be constructed
static int foo = myInitializer(); // myInitializer() might not be called  
static QString bar("hello"); // as above
 
static int foo = myInitializer(); // myInitializer() might not be called
</code>
; Correct
; Correct
static const int i = 42;
<code cppqt>
static const int ii[3] = {1, 2, 3};
static const int i = 42;
static const char myString[] = "hello";
static const int ii[3] = {1, 2, 3};
static const MyStruct s = {3, 4.4, "hello"};
static const char myString[] = "hello";
static const MyStruct s = {3, 4.4, "hello"};
</code>


You can use <code>Q_GLOBAL_STATIC</code> to create global static objects which will be initialized the first time you use them.
You can use <tt>Q_GLOBAL_STATIC</tt> to create global static objects which will be initialized the first time you use them.


== Signal and Slot Normalization ==
== Signal and Slot Normalization ==
Since <code>QObject::connect</code> uses a string-based comparison
Since <tt>QObject::connect</tt> uses a string-based comparison
of the function signature, it requires some normalization to take
of the function signature, it requires some normalization to take
place. It does that automatically for you, but it takes some CPU
place. It does that automatically for you, but it takes some CPU
Line 168: Line 180:


For example, you may have the following code:
For example, you may have the following code:
QObject::connect(this, SIGNAL( newValue(const QString&,
<code cppqt>
                                        const MyNamespace::Type&) ),
QObject::connect(this, SIGNAL( newValue(const QString&,
                  other, SLOT( value(const QString &) ));
                                        const MyNamespace::Type&) ),
 
                other, SLOT( value(const QString &) ));
</code>
It would be preferrable to write as follows:
It would be preferrable to write as follows:
QObject::connect(this, SIGNAL(newValue(QString,Type)),
<code cppqt>
                  other, SLOT(value(QString)));
QObject::connect(this, SIGNAL(newValue(QString,Type)),
                other, SLOT(value(QString)));
</code>


Note the absence of namespace markers, extra whitespace and the
Note the absence of namespace markers, extra whitespace and the
Line 184: Line 199:


'''Note''': If you are unsure about the normalization, don't do it. Let
'''Note''': If you are unsure about the normalization, don't do it. Let
QObject do it for you (the performance penalty is negligible in most cases).
QObject do it for you (the performance penalty is negligible in most cases).





Revision as of 18:00, 21 December 2006

This document describes some of the recommended conventions that should be applied in the KDE libraries (not applications). Respecting these guidelines helps create a consistant API and also may help ease maintainence of the libraries later. While these conventions are not mandatory, they are important guidelines, and should be respected unless you have a good reason to disregard them.

As an introduction, you should read the document Designing Qt-Style C++ APIs.

Naming Conventions

In KDE, we basically follow the same naming conventions as Qt.

Class names starts with a capital K. The rest is in camel case. Function names starts with a lower case, but the first letter of each successive word is capitalized.

Unless dealing with central libraries (kdecore, kdeui), classes should be in the library namespace. In that case, it is the namespace which starts with K and the classes inside may not start with it. New libraries should choose their namespace.

The prefix 'set' is used for setters, but the prefix 'get' is not used for accessors. Accessors are simply named with the name of the property they access. The exception is for accessors of a boolean which may start with the prefix 'is'.

Acronyms are lowercased too. Example: KUrl instead of KURL and isNssEnabled() instead of isNSSEnabled()

Accessors should usually be const.

This example shows some possible functions names public:

   void setColor(const QColor& c);
   QColor color() const;
   void setDirty(bool b);
   bool isDirty() const;

private Q_SLOTS:

   void slotParentChanged();

Make one public class for every .h file. Add the _EXPORT macro related to the library they are in. Private classes should be declared in the .cpp file, or in a _p.h file.

D-Pointers

In order to more easily maintain binary compatibility, there shouldn't be private members in a public class. For more information about binary compatibility, read Compatibility Issues With C++.

By convention, the private class will be named the same as the public class, with Private appended to the name. class KFooPrivate; class KFoo {

   public:
       /* public members */
   private:
       KFooPrivate * const d;

}; In the .cpp file: class KFooPrivate {

   public:
       int someInteger;

};

KFoo::KFoo() : d(new KFooPrivate) {

   /* ... */

}

KFoo::~KFoo() {

   delete d;

} Notice that the member d is const to avoid modifying it by mistake.

If you are implementing an implicitly shared class, you should consider using QSharedData and QSharedDataPointer for d. If you don't use them, then use QAtomic for reference counting. Don't try to implement your own refcounting with integers.

Sometimes, complex code may be moved to a member method of the Private class itself. Doing this may give the compiler an extra register to optimize the code, since you won't be using "d" all the time.

Inline Code

For binary compatibility reasons, try to avoid inline code in headers. Specifically no inline constructor or destructor.

If ever you add inline code please note the following:

  • Installed headers should compile with the following preprocessor defines: QT_NO_CAST_FROM_ASCII, QT_NO_CAST_TO_ASCII, QT_NO_KEYWORD. So don't forget QLatin1String.
  • No C casts in the header. Use static_cast if types are known. Use qobject_cast instead of dynamic_cast if types are QObject based. dynamic_cast is not only slower, but is also unreliable across shared libraries.
  • In general, check your code for common mistakes.

These recommendations are also true for code that are not in headers.

Flags

Try to avoid meaningless boolean parameters in functions. Example of a bad boolean argument:

static QString KApplication::makeStdCaption(const QString &userCaption,
                                            bool withAppName = true,
                                            bool modified = false);

Because when you read code that uses the above function, you can't easily know the significance of the parameters

window->setCaption(KApplication::makeStdCaption("Document Foo", true, true));

The solution is to use QFlags. If the options only apply to one function, call the enum FunctionNameOption and the QFlags typedef FunctionNameOptions. Do that even if there is only one option, this will allow you to add more options later and keep the binary compatibility.

So a better API would be: class KApplication {

   public:
       /* [...] */
       enum MakeStandardCaptionOption {
           /**
            * Indicates that the method shall include the application name
            */
           WithApplicationName = 0x01,
           /**
            * If set, a 'modified' sign is included in the returned string.
            */
           Modified = 0x02
       };
       Q_DECLARE_FLAGS(MakeStandardCaptionOptions, MakeStandardCaptionOption)
       /**
        * Builds a caption using a standard layout.
        *
        * @param userCaption The caption string you want to display
        * @param options a set of flags from MakeStandartCaptionOption
        */
       static QString makeStandardCaption(const QString& userCaption,
           const MakeStandardCaptionOptions& options = WithApplicationName);
       /* [...] */

}; Q_DECLARE_OPERATORS_FOR_FLAGS(KApplication::MakeStandardCaptionOptions)

Const References

Each object parameter that is not a basic type (int, float, bool, enum, or pointers) should be passed by reference-to-const. This is faster, because it is not required to do a copy of the object. Do that even for object that are already implicitly shared, like QString:

QString myMethod(const QString& foo, const QPixmap& bar, int number);

Signals and Slots

In the libraries, use Q_SIGNALS and Q_SLOTS instead of signals and slots. They are syntactically equivalent and should be used to avoid conflicts with boost signals, and with python's use of "slots" in its headers.

Properites

Consider using Q_PROPERTY for properties. The reason is that properties (especially thoses marked SCRIPTABLE) will be accessible through the javascript interface.

If you follow the propname / setPropname naming sheme, moc sets a special flag for the QMetaProperty.

Explicit Constructors

For each constructor (other than the copy constructor), check if you should make the constructor explicit in order to minimize wrong use of the constructor.

Basically, each constructor that may take only one argument should be marked explicit unless the whole point of the constructor is to allow implicit casting.

Avoid including other headers in headers

Try to reduce as much as possible the number of includes in header files. This will generally help reduce the compilation time, especially for developers when just one header has been modified. It may also avoid errors that can be caused by conflicts between headers.

If an object in the class is only used by pointer or by reference, it is not required to include the header for that object. Instead, just add a forward declaration before the class.

In this example, the class KFoo uses KBar by reference, so we do not need to include KBar's header:

  1. include <kfoobase.h>

class KBar; class KFoo : public KFooBase {

   public:
       /* [...] */
       void myMethod(const KBar& bar);

};

Static Objects

Global static objects in libraries should be avoided. You never know when the constructor will be run or if it will be run at all.

Wrong

static QString foo; // wrong - object might not be constructed static QString bar("hello"); // as above static int foo = myInitializer(); // myInitializer() might not be called

Correct

static const int i = 42; static const int ii[3] = {1, 2, 3}; static const char myString[] = "hello"; static const MyStruct s = {3, 4.4, "hello"};

You can use Q_GLOBAL_STATIC to create global static objects which will be initialized the first time you use them.

Signal and Slot Normalization

Since QObject::connect uses a string-based comparison of the function signature, it requires some normalization to take place. It does that automatically for you, but it takes some CPU time, so, if it doesn't hurt your code's readability, normalize manually your SIGNAL and SLOT entries.

For example, you may have the following code: QObject::connect(this, SIGNAL( newValue(const QString&,

                                       const MyNamespace::Type&) ),
                other, SLOT( value(const QString &) ));

It would be preferrable to write as follows: QObject::connect(this, SIGNAL(newValue(QString,Type)),

                other, SLOT(value(QString)));

Note the absence of namespace markers, extra whitespace and the reduction of pass-by-reference-to-const parameters to simple pass-by-value ones. The normalization may involve other transformations, but these are the most common ones. To be sure what the proper normalization is, read the .moc file generated for the class.

Note: If you are unsure about the normalization, don't do it. Let QObject do it for you (the performance penalty is negligible in most cases).


Documentation

Every class and method should be well documented. Read the KDE Library Documentation Policy for the guidelines to follow when documenting your code.

Also don't forget the license headers and copyrights in each file. As stated in the Licensing Policy, kdelibs code must be licensed under the LGPL, BSD, or X11 license.

Author: Olivier Goffart Mart 2006