X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b953bdc2934895fa31d859be25be1f850701c2c3..3b50432abf11a0dbdab6b8d4136c3956ce4def1f:/docs/html/standard.htm diff --git a/docs/html/standard.htm b/docs/html/standard.htm new file mode 100644 index 0000000000..196b63ff5a --- /dev/null +++ b/docs/html/standard.htm @@ -0,0 +1,953 @@ + +
++ +wxWindows Programmer Style Guide + + | +
+ +by Vadim Zeitlin
+ +This guide is intended for people who are (or intending to start) writing code +for wxWindows class library. + +
+The guide is separated into two parts: the first one addresses the general +compatibility issues and is not wxWindows-specific. The advises in this part +will hopefully help you to write programs which compile and run on greater +variety of platforms. The second part details the wxWindows code organization and +its goal it to make wxWindows as uniform as possible without imposing too +many restrictions on the programmer. +
+Acknowledgements: This guide is partly based on +C++ portability guide by David Williams. + +
+
+ +
The usage of all features in this section is not recommended for one reason: they appeared in C++ relatively recently and are not yet +supported by all compilers. Moreover, when they're supported, there are +differences between different vendor's implementations. It's understandable that +you might love one (or all) of these features, but you surely can write C++ +programs without them. Where possible, workarounds to compensate for absence +of your favourite C++ abilities are indicated. +
Just to suppress any doubts that there are compilers which don't support +these new features, you can think about Win16 (a.k.a. Win 3.1) compilers, +none of which supports any feature from the list below. + +
+Besides the reasons mentioned above, template usage also makes the +program compile much slower (200%-300% is not uncommon) and their support +even in the compilers which have had it for a long time is far from perfect +(the best example is probably gcc). +
Workaround: The things you would like to use templates for are, +most commonly, polymorphic containers (in the sense that they can contain objects of +any type without compromising C++ type system, i.e. using void * +is out of question). wxWindows provides dynamic +arrays and lists which are sufficient in 99% of cases - please don't hesitate +to use them. Lack of template is not a reason to use static arrays or +type-less (passing by void *) containers. + +
+The C++ exception system is an error-reporting mechanism. Another reasons not to use it, +besides portability, are the performance penalty it imposes (small, but, at least for +current compilers, non-zero), and subtle problems with +memory/resource deallocation it may create (the place where you'd like to use +C++ exceptions most of all are the constructors, but you need to be very +careful in order to be able to do it). +
Workaround: there is no real workaround, of course, or the exceptions +wouldn't have been added to the language. However, there are several rules which +might help here:
+ +
There is no such thing as a function that never fails - even if it can't + fail now, it might do it later, when modified to be more powerful/general. + Put the int or bool return type from the very beginning!
+
Never rely on the function's success, always test for a possible error.
+
Exceptions are always caught and, normally, processed when they're + caught. In the same manner, the error return code must always be processed + somehow. You may choose to ignore it, but at least tell the user that + something wrong happened using wxLogError or + wxLogWarning functions. All wxWindows + functions (must) log the error messages on failure - this can be disabled + by using wxLogNull object before calling it. +
Examples:
+void ReadAddressBookFile(const wxString& strName) +{ + wxFile file; + + if ( !file.Open(strFile) ) + return; + + ...process it... +} ++
+// returns false if the address book couldn't be read +bool ReadAddressBookFile(const wxString& strName) +{ + wxFile file; + + if ( !file.Open(strFile) ) { + // wxFile logged an error because file couldn't be opened which + // contains the system error code, however it doesn't know what + // this file is for and an error message "can't open $GLCW.ADB" + // can be quite confusing for the user. Here we say what we mean. + wxLogError("Can't read address book from '%s'!", + strName.c_str()); + return false; + } + + ...process it... + + return true; +} ++ or, if it's not an error if file doesn't exist (here we could just check + its existence, but let's suppose that there is no wxFile::Exists()) + we can also write: +
+// returns false if address book file doesn't exist +bool ReadAddressBookFile(const wxString& strName) +{ + wxFile file; + + // start a block inside which all log messages are suppressed + { + wxLogNull noLog; + if ( !file.Open(strFile) ) + return false; + } + + ...process it... + + return true; +} +
+RTTI stands for Run-Time Type Information and there is probably no other +reason not to use it except the portability issue and the fact that it adds +sizeof(void *) bytes to any class having virtual functions (at least, +in the implementations I'm aware of). +
Workaround: use wxWindows RTTI system which allows you to do almost +everything which the new C++ RTTI, except that, of course, you have to use +macros instead of the (horrible looking, BTW) dynamic_cast. + +
+This topic is subject to change with time, however for the moment all wxWindows +classes/functions live in the global namespace. +
Workaround: None. + +
+STL is the new C++ standard library, proposing all kinds of template containers +and generic algorithm implementations. Templates are the heart (and almost +everything else) of the library, so its usage is out of question. Besides, even +with the compilers which do support templates, STL has many of its own problems, +there are many "not 100% standard compatible" vendor implementations, none of existing debuggers understands its +complicated data structures, ... the list can go on (almost) forever. +
Workaround: Use wxString, dynamic arrays and lists and other wxWindows +classes. wxString has many of the most often used functions of std::string STL +class (typedef to be precise). +
+The scope of a variable declared inside for() statement changed several +years ago, however many compilers still will complain about second declaration +of i in the following code: +
+ for ( int i = 0; i < 10; i++ ) { + ... + } + + ... + + for ( int i = 0; i < 10; i++ ) { + ... + } ++even though if it's perfectly legal now. +
Workaround: write this instead: +
+ int i; + for ( i = 0; i < 10; i++ ) { + ... + } + + ... + + for ( i = 0; i < 10; i++ ) { + ... + } ++or, even better, use different names for the variables in the different for +loops (in particular, avoid mute variable names like i above) - then +you can declare them in the loop statement and don't pollute the outer name +space with local loop variables. + +
+Nested classes are, without doubt, a very good thing because they allow to hide +"private" (in the sense that they're used only inside the library) classes and, +generally, put the related things together. +
Unfortunately, some compilers have trouble understanding them, so we must +sacrifice the ideals of software design to get a working program in this case. +
Workaround: instead of +
+ // in the header + class PublicLibClass { + ... + private: + class PrivateLibClass { ... } m_object; + }; ++you can try the following: +
+ // in the header + class PrivateLibClass; // fwd decl + class PublicLibClass { + ... + private: + class PrivateLibClass *m_pObject; + }; + + // in the .cpp file + class PrivateLibClass { ... }; + + PublicLibClass::PublicLibClass() + { + m_pObject = new PrivateLibClass; + + ... + } + + PublicLibClass::~PublicLibClass() + { + delete m_pObject; + } ++
A nice side effect is that you don't need to recompile all the files +including the header if you change the PrivateLibClass declaration (it's +an example of a more general interface/implementation separation idea). + +
+The C++ standard has introduced the following new reserved words: or, +and, not, xor, bitand, bitor, +compl, and_eq, or_eq, not_eq, +or_eq which can be used instead of the usual C operations &&, +||, ~ etc. +
This wonderful (and not backwards compatible in addition to being +absolutely useless) new feature means that these new keywords should not be +used as the variable names - even if current compilers usually will accept +this, your code will break in the future. For most of the keywords, using them +as variable names is quite unlikely, but or and compl were +used in the wxWindows sources which seems to indicate that they are the most +likely candidates. +
It goes without saying that these new keywords should not be used instead +of the tradional C operators neither both because most compilers don't accept +them and because using them in C code makes it less readable. +
+This section lists the less obvious limitations of the current C++ compilers +which are less restrictive than the ones mentioned in the previous section but +are may be even more dangerous as a program which compiles perfectly well on +some platform and seems to use only standard C++ featurs may still fail to +compile on another platform and/or with another compiler. + +
+ The ternary operator ?: shouldn't be used with objects (i.e. if any +of its operands are objects) because some compilers (notably Borland C++) fail +to compile such code. +
Workaround: use if/else instead. +
+ wxString s1, s2; + + // Borland C++ won't compile the line below + wxString s = s1.Len() < s2.Len() ? s1 : s2; + + // but any C++ compiler will compile this + wxString s; + if ( s1.Len() < s2.Len() ) + s = s1; + else + s = s2; ++ +
+The initializers for automatic array variables are not supported by some older +compilers. For example, the following line +
+ int daysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; ++will fail to compile with HP-UX C++ compiler. +
Workaround: either make the array static or initialize each item +separately: in the (stupid) example above, the array should be definitely +declared as static const (assuming that the leap years are dealt with +elsewhere somehow...) which is ok. When an array is really not const, you +should initialize each element separately. + +
+It is a good rule to follow in general, but some compilers (HP-UX) enforce it. +So even if you are sure that the default constructor for your class is ok but +it has a destructor, remember to add an empty default constructor to it. +
+While the recommendations in the previous section may not apply to you if you're +only working with perfect compilers which implement the very newest directives of +C++ standard, this section contains compiler- (and language-) independent advice +which must be followed if you wish to write correct, i.e. working, programs. It +also contains some C/C++ specific remarks in the end which are less +important. +
+Never use C++ comments in C code - not all C compilers/preprocessors +understand them. Although we're mainly concerned with C++ here, there are +several files in wxWindows sources tree which are compiled with C compiler. +Among them are include/wx/setup.h and include/wx/expr.h. + +Another thing related to C vs C++ preprocessor differences is that some old C +preprocessors require that all directives start in the first column (while +it's generally allowed to have any amount of whitespace before them in C++), +so you should start them in the beginning of the line in files which are +compiled with C compiler. + +
+In C++, the constructors of global variables are called before the +main() function (or WinMain() or any other program entry point) +starts executing. Thus, there is no possibility to initialize anything +before the constructor call. The order of construction is largely +implementation-defined, meaning that there is no guarantee that one global +object will be initialized before another one (except if they are both defined +in the same translation unit, i.e. .cpp file). Most importantly, no custom +memory allocation operators are installed at the moment of execution of global +variables constructors, so a (less restrictive) rule is that you should have +no global variables which allocate memory (or do anything else non-trivial) in +the constructor. Of course, if an object doesn't allocate memory in its constructor +right now, it may start making it later, so you can only be sure about this if +you don't use any variables of object (as opposed to simple: +int, ...) types. Example: currently, wxString doesn't allocate memory +in its default constructor, so you might think that having a global (initially) +empty wxString is safe. However, if wxString starts allocating some minimal +amount of memory in its default constructor (which doesn't look unreasonable), +you would have all kinds of problems with new +and delete operators (overloaded in wxWindows), especially because the first new called +is the standard one (before wxWindows overloads them) and delete will +be the overloaded operator. + +
+Give the compiler a chance to help you - turn on all warnings! You should always +use the maximum available warning level of your compiler and understand and +correct each of them. If, for whatever reasons, a compiler gives a warning on +some perfectly legal line of code and you can't change it, please insert a +comment indicating it in the code. Most oftenly, however, all compiler warnings +may be avoided (not suppressed!) with minimal changes to your code. + +
+You should never assume any absolute constraints on data type sizes. Currently, +we have 16-bit, 32-bit and 64-bit machines and even inside each class data type +sizes are different. A small table illustrates it quite well: +
Architecture/OS | +sizeof(short) | +sizeof(int) | +sizeof(long) | +sizeof(void *) | +
i386/Windows 3.1 | +2 | +2 | +4 | +2 or 4 | +
i386/Windows 95 | +2 | +4 | +4 | +4 | +
Merced/Win64 | +2 | +4 | +4 | +8 | +
Alpha/Linux | +??? | +??? | +??? | +??? | +
+Although close to the heart of many C programmers (I plead guilty), code like +classical if ( (c = getchar()) != EOF ) is bad because it prevents you +from enabling "assignment in conditional expression" warning (see also +above) which is helpful to detect common +mistypes like if ( x = 2 ) instead of if ( x == 2 ). + +
+If you have to temporarily disable some code, use +
+ #if 0 // VZ: I think this code is unneeded, it probably must be removed + ... + #endif // 0 ++instead of +
+ /* + ... + */ ++The reason is simple: if there are any /* ... */ comments inside +... the second version will, of course, miserably fail. + +
+ +You should avoid having overloaded virtual methods in a base class because if +any of them is overriden in a derived class, then all others must be overriden +as well or it would be impossible to call them on an object of derived class. + +For example, the following code: + +
+ class Base + { + public: + virtual void Read(wxFile& file); + virtual void Read(const wxString& filename); + }; + + class Derived : public Base + { + public: + virtual void Read(wxFile& file) { ... } + }; + + ... + + Derived d; + d.Read("some_filename"); // compile error here! ++ +will fail to compile because the base class function taking filename +is hidden by the virtual function overriden in the derived class (this is +known as [virtual] function name hiding problem in C++). + +
+The standard solution to this problem in wxWindows (where we have such +situations quite often) is to make both Read() functions not virtual +and introduce a single virtual function DoRead(). Usually, it makes +sense because the function taking a filename is (again, usually) implemented +in terms of the function reading from a file anyhow (but making only this +functions not virtual won't solve the above problem!). +
+So, the above declarations should be written as: +
+ class Base + { + public: + void Read(wxFile& file); + void Read(const wxString& filename); + + protected: + virtual void DoRead(wxFile& file); + }; + + class Derived : public Base + { + protected: + virtual void DoRead(wxFile& file) { ... } + }; ++ +This technique is widely used in many of wxWindows classes - for example, +wxWindow has more than a dozen of DoXXX() functions which +allows to have many overloaded versions of commonly used methods such as +SetSize() + +
+Some compilers don't pay any attention to extra semicolons on top level, as in +
+ class Foo { };; ++while others complain loudly about it. Of course, you would rarely put 2 +semicolons yourself, but it may happen if you're using a macro +(IMPLEMENT_something, for example) which already has a ';' inside and +put another one after it. +
+ Two operating systems supported by wxWindows right now are (different flavours +of) Unix and Windows 3.1/95/NT (although Mac, OS/2 and other ports exist/are +being developed as well). The main differences between them are summarized +here. + +
+There is, unfortunately, no standard exceptions for C++ source files. Different +people use .C, .cc, .cpp, .cxx, .c++ and probably several others I forgot. Some +compilers don't care about extension, but there are also other ones which can't +be made to compile any file with "wrong" extension. Such compilers are very +common in DOS/Windows land, that's why the .cpp extension is the least likely to +cause any problems - it's the standard one under DOS and will probably be +accepted by any Unix compiler as well (any counter examples?). The extension +for the header files is .h. + +
+Although it's too silly to mention, please don't use backslashes in +#include preprocessor statement. Even not all Windows compilers accept +it, without speaking about all other ones. + +
+This problem will hopefully not arise at all, with CVS taking care of this
+stuff, however it's perhaps not useless to remember that many Unix compilers
+(including, but not limited to, gcc) don't accept carriage returns
+(=
+DOS/Windows 3.1 isn't case sensitive, Windows 95/NT are case preserving, but not
+case sensitive. To avoid all kinds of problems with compiling under Unix (or
+any other fully case-sensitive OS), please use only lower case letters in the
+filenames.
+
+
+While DOS/Windows compilers don't seem to mind, their Unix counterparts don't
+like files without terminating new-line. Such files also give a warning message
+when loaded to vim (the Unix programmer's editor of choice :-)), so please think
+about terminating the last line.
+
+
+The linker on VMS is case-insensitive. Therefore all external variables and
+functions which differ only in case are not recognized by the linker as
+different, so all externals should differ in more than the case only:
+i.e. GetId is the same as GetID.
+
+
+ All wxWindows specific style guidelines are specified in the next +section, here are the choices which are not completely arbitrary, +but have some deeper and not wxWindows-specific meaning. + +
+We all know how important it is to write readable code. One of the first steps +in this direction is the choice of naming convention. It may be quite vague or +strictly define the names of all the variables and function in the program, +however it surely must somehow allow the reader to distinguish between +variable and functions and local variables and member variables from the first +glance. +
The first requirement is commonly respected, but for some strange reasons, the +second isn't, even if it's much more important because, after all, the immediate +context usually allows you to distinguish a variable from a function in +C/C++ code. On the other hand, you cannot say what x in the +following code fragment is: +
+ void Foo::Bar(int x_) + { + ... + + x = x_; + + ... + } ++It might be either a local variable (unluckily the function is too long so you +don't see the variable declarations when you look at x = x_ line), a +member variable or a global variable - you have no way of knowing. +
The wxWindows naming convention gives you, the reader of the code, much more +information about x. In the code above you know that it's a local +variable because:
+
Examples: +
+ extern int g_x; // of course, 'x' is not the best name for a global... + + void Bar() + { + int x; + } + + class Foo { + public: + void SetX(int x) { m_x = x; } + private: + int m_x; + }; ++As you see, it also solves once and for all the old C++ programmer's question: +how to call SetX() parameter? The answer is simple: just call it +x because there is no ambiguity with Foo::m_x. +
The prefixes can be combined to give ms_ and gs_ for static +member (a.k.a. class) variables and static global variables. +
The convention is, of course, completely worthless if it is not followed: +nothing like being sure that x is a local variable in the code fragment +above and discovering later the following lines in the header: +
+ class Foo { + ... + int x; // I don't like wxWindows naming convention + }; ++Please do use these prefixes, they make your code much easier to read. Also +please notice that it has nothing to do with the so-called Hungarian notation +which is used in wxMSW part of wxWindows code and which encodes the type +of the variable in its name - it is actually quite useful in C, but has little +or no sense in C++. + +
+In ANSI C, void Foo() takes an arbitrary number of arbitrarily typed +arguments (although the form void Foo(...) is preferred) and void +Foo(void) doesn't take any arguments. In C++, however, the situation is +different and both declarations are completely equivalent. As there is no need +to write void in this situation, let's not write it - it can only be +confusing and create an impression that it really means something when it's not +at all the case. + +
+In both C and C++ an argument passed by value cannot be modified - or, more +precisely, if it is modified in the called function, only the local copy is +really changed, not the caller's variable. So, semantically speaking, there is +no difference between void Foo(int) and void Foo(const int). +However, the const keyword is confusing here, adds nothing to the code +and even cannot be removed if Foo() is virtual and overridden (because +the names are mangled differently). So, for arguments passed by value +you shouldn't use const. +
Of course, it doesn't apply to functions such as +void PrintMessage(const char *text) where const is mandatory. +
+ +
+
+The wxWindows files for each supported platform have their own subdirectories +in "include" and "src". So, for example, there is "src/msw", "include/gtk" +etc. There are also two special subdirectories called "common" and +"generic". The common subdirectory contains the files which are platform +independent (wxObject, wxString, ...) and the generic one the generic +implementations of GUI widgets, i.e. those which use only other wxWindows +classes to implement them. For the platforms where the given functionality +cannot be implemented natively, the generic implementation is used and the native +one is used for the others. As I feel that it becomes a bit too confusing, +here is an example: wxMessageBox function is implemented natively under +Windows (where it just calls MessageBox API), but there is also a generic +implementation which is used under, for example, GTK. A generic class should +normally have a name that distinguishes it from any platform-specific implementation. +A #define will allow wxGenericMessageDialog to be wxMessageDialog on some +platforms, for example. + +
This scheme applies not only for the .cpp files, but also for the headers. +However, as the program using wxWindows should (ideally) not use any +"#ifdef <platform>" at all, the headers are always included with +"#include <wx/msgdlg.h>" (for example). This file, in turn, includes +the right header for given platform. Any new headers should conform to this +setup as well to allow including <wx/foo.h> on any platform.
+ +Note that wxWindows implementation files should use quotes when including wxWindows +headers, not angled brackets. Applications should use angled brackets. This +ensures that the dependencies are correctly handled by the compiler. + +
+To minimize the compile time C++ programmers often use so called include +guards: for example, in the header file foo.h you might have + +
+#ifndef _FOO_H_ +#define _FOO_H_ + +... all header contents ... + +#endif + //_FOO_H_ ++ +In this way, the header will only be included once for the compilation +of any .cpp (of course, it still will be included many times for the +compilation of the whole project, so it has nothing to do with precompiled +headers). wxWindows is no exception and also uses include guards which should use +the above form, except for top-level headers which include files with identical +names, in which case you should use _FOO_H_BASE_. + +
+The precompiled headers greatly (we're speaking about orders of hundreds of
+percent here) reduce the compilation time. wxWindows uses them if the target
+compiler supports them (it knows about MS Visual C++, Borland C++ and g++).
+You should include all the headers included from
+ +The start of a cpp implementation file after the heading might look like this:
+ +
+#ifdef __GNUG__ +#pragma implementation "bitmap.h" +#endif + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include <stdio.h> +#include "wx/setup.h" +#include "wx/list.h" +#include "wx/utils.h" +#include "wx/app.h" +#include "wx/palette.h" +#include "wx/bitmap.h" +#include "wx/icon.h" +#endif + +#include "wx/msw/private.h" +#include "assert.h" ++ + +
Any header file should containg the following lines: +
+#ifdef __GNUG__ + #pragma interface "foo.h" +#endif ++and the corresponding .cpp file: +
+#ifdef __GNUG__ + #pragma implementation "foo.h" +#endif +for g++ compilation. +
+
+
+
+
+ +
+bool WXDLLEXPORT wxYield(void); +class WXDLLEXPORT MyClass; // (for forward declarations and real declarations) +WXDLLEXPORT_DATA(extern wxApp*) wxTheApp; ++ +The reason for the strange syntax for data is that some compilers use different +keyword ordering for exporting data. + +
+There is a convention in wxWindows to prefix the accessors (i.e. any simple, in +general, inline function which does nothing else except changing or returning +the value of a member variable) with either Set or Get. + +
+The constants in wxWindows code should be defined using enum C++ +keyword (and not with #define or static const int). They +should be declared in the global scope (and not inside class declaration) and +their names should start with a wx prefix. Finally, the constants +should be in all capital letters (except the first 2) to make it easier to +distinguish them from the variables with underscores separating the words. + +
For example, file-related constants should be declared like this: +
+enum +{ + wxFILEOPEN_READ, + wxFILEOPEN_WRITE, + wxFILEOPEN_READWRITE +}; ++ +
+
+It's really a trivial piece of advice, but remember that using forward declarations +instead of including the header of corresponding class is better because not +only does it minimize the compile time, it also simplifies the dependencies +between different source files. +
On a related subject, in general, you should try not to include other +headers from a header file. + +
+wxWindows provides the debugging macros wxASSERT, wxFAIL and
+wxCHECK_RET in
Also, please use wxFAIL_MSG("not implemented") instead of writing +stubs for not (yet) implemented functions which silently return incorrect +values - otherwise, a person using a not implemented function has no idea that +it is, in fact, not implemented. +
As all debugging macros only do something useful if the symbol +__WXDEBUG__ is defined, you should compile your programs in debug mode to profit +from them. +
+ +