Policies/Library Code Policy: Difference between revisions

From KDE TechBase
(Move the example out because it's too long)
(Replaced content with "{{Moved To Community}}")
 
(32 intermediate revisions by 15 users not shown)
Line 1: Line 1:
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.
{{Moved To Community}}
 
As an introduction, you should read the document [http://doc.trolltech.com/qq/qq13-apis.html Designing Qt-Style C++ APIs].
 
For kdelibs, it is recommended to follow the [[Policies/Kdelibs_Coding_Style | Kdelibs Coding Style]].
 
== 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:
<tt>KUrl</tt> instead of <tt>KURL</tt> and <tt>isNssEnabled()</tt> instead of <tt>isNSSEnabled()</tt>
 
Accessors should usually be <tt>const</tt>.
 
This example shows some possible functions names
<code cppqt>
public:
    void setColor(const QColor& c);
    QColor color() const;
    void setDirty(bool b);
    bool isDirty() const;
 
private Q_SLOTS:
    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.
 
== 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 [[Policies/Binary_Compatibility_Issues_With_C++|Binary Compatibility Issues With C++]].
 
By convention, the private class will be named the same as the public class, with <tt>Private</tt> appended to the name.
<code cppqt>
class KFooPrivate;
class KFoo
{
public:
    /* public members */
private:
    KFooPrivate * const d;
};
</code>
In the .cpp file:
<code cppqt>
class KFooPrivate
{
    public:
        int someInteger;
};
 
KFoo::KFoo() : d(new KFooPrivate)
{
    /* ... */
}
 
KFoo::~KFoo()
{
    delete d;
}
</code>
Notice that the member d is <tt>const</tt> 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.
 
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.
 
=== Shared D-Pointers ===
 
If your class hierarchy is large and/or deep, you may want to try the concept of shared d-pointers. You'll be trading the added complexity for a smaller memory footprint in the main object (there will be only one "d" variable in it). Other advantages include:
 
:* direct access to the private data of the whole hierarchy (in other words, the Private classes are in fact "protected", not "private")
:* access to the parent's d-pointer methods
 
The latter advantage is especially useful if your class has moved the code from the main class to the Private class. If that's the case, you should be calling the Private methods instead: since they are not exported, they will create simpler relocations in the final library (or none at all). By simply calling the Private method instead of the public one, you contribute to a faster load-time of your library.
 
To implement a "shared d-pointer", you need to:
:# define a '''protected''' variable (d_ptr) in the least derived class of your hierarchy
:# in each class of the hierarchy, define a '''private''' function called d_func() that reinterpret_casts that d_ptr to the current class's Private class
:# use Q_D(Foo) at the beginning of the functions to have access to a variable "d"
:# the private classes derive from one another just like the public hierarchy; they also have virtual destructors
:# add one extra, protected constructor that takes the private class as a parameter
:# in each constructor for all derived classes, call the parent's constructor that takes the d pointer as a parameter
 
There's an example of such a construct in a [[/Shared_D-Pointer_Example|separate page]].
 
=== Q_DECLARE_PRIVATE ===
 
This is a handy macro that hides the ugly stuff for you. It creates the <tt>d_func()</tt> function for you, using the variable called <tt>d_ptr</tt>. If yours has that name, you can use this macro. If it has another name, maybe you should create a macro to make your code look nicer.
 
=== Q-Pointers ===
 
Q-pointers are like d-pointers, but work in the reverse direction: they are in the Private class and they point to the public class. Needless to say, this is only possible for classes that don't share their d-pointers. Examples of classes that might benefit from q-pointers are all those derived from QObject, while classes with implicit sharing are those that potentially can't use it.
 
Q-pointers are especially useful if your class has moved most of the code to the Private class as recommended. In that case, you may need to emit signals from the Private class. You would do it as:
 
<code cppqt>
    emit q->signalName();
</code>
(You need to declare the Private class a friend of your public one; Q_DECLARE_PRIVATE does that for you)
 
Q-pointers may also use the a shared q-pointer technique just like [[#Shared D-Pointers|d-pointers]] can. What's more, Qt also provides a macro called <tt>Q_DECLARE_PUBLIC</tt> and one <tt>Q_Q</tt> to hide the ugly parts of the implementation.
 
== 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: <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 <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].
 
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:
<code cppqt>
static QString KApplication::makeStdCaption( const QString &caption,
                                            bool withAppName,
                                            bool modified);
</code>
 
Because when you read code that uses the above function, you can't easily know the significance of the parameters
 
<code cppqt>
window->setCaption(KApplication::makeStdCaption( "Document Foo",
                        true, true));
</code>
 
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:
<code cppqt>
class KApplication
{
public:
    /* [...] */
    enum StandardCaptionOption {
        /**
        * Indicates to include the application name
        */
        WithApplicationName = 0x01,
        /**
        * Note in the caption that there is unsaved data
        */
        Modified = 0x02
    };
    Q_DECLARE_FLAGS(StandardCaptionOptions,
                    StandardCaptionOption)
 
    /**
    * 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 StandardCaptionOptions& options = WithApplicationName);
    /* [...] */
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KApplication::StandardCaptionOptions)
</code>
 
== 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:
 
<code cppqt>
QString myMethod( const QString& foo,
                  const QPixmap& bar,
                  int number );
</code>
 
== Signals and Slots ==
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.
 
== Properties ==
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}}.
 
== Explicit Constructors ==
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 <tt>explicit</tt> 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:
<code cppqt>
#include <kfoobase.h>
class KBar;
class KFoo : public KFooBase
{
    public:
        /* [...] */
        void myMethod(const KBar& bar);
};
</code>
 
== 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
<code cppqt>
static QString foo; // wrong - object might not be constructed
static QString bar("hello"); // as above
static int foo = myInitializer(); // myInitializer() might not be called
</code>
; Correct
<code cppqt>
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"};
</code>
 
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 ==
Since <tt>QObject::connect</tt> 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:
<code cppqt>
QObject::connect(this, SIGNAL( newValue(const QString&,
                                        const MyNamespace::Type&) ),
                other, SLOT( value(const QString &) ));
</code>
It would be preferrable to write as follows:
<code cppqt>
QObject::connect(this, SIGNAL(newValue(QString,Type)),
                other, SLOT(value(QString)));
</code>
 
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 {{path|.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 [[Policies/Library Documentation Policy|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 [[Policies/Licensing Policy|Licensing Policy]], kdelibs code must be licensed under the LGPL, BSD, or X11 license.
 
Author: [mailto:[email protected] Olivier Goffart] March 2006
 
[[Category:Policies]]

Latest revision as of 18:17, 10 March 2016

This page is now on the community wiki.