view codeDoc/policies.txt @ 131:9cff74f68b84

Major revisions to popup handling. Buttons can close menus now, plus some smaller impovements. Removed Widget module. Moved Widget.AWidget to AChildWidget.AChildWidget and Widget.AParentWidget to AParentWidget.AParentWidget. Removed ASingleParentWidget to improve code sharing. AChildWidget doesn't implement IParentWidget like AWidget did. New IPopupParentWidget extending IParentWidget for the WM and some widgets to handle popups. Cut old popup management code. New underMouse() function replacing highlight(); called on all widgets. Separate menu-popup and button widgets aren't needed for menus now. Functions returning content widgets have been moved to their own module. Cleaned up jobs.txt. Switched to 80 line length for Ddoc.
author Diggory Hardy <diggory.hardy@gmail.com>
date Wed, 21 Jan 2009 13:01:40 +0000
parents 57d000574d75
children 24d77c52243f
line wrap: on
line source

--- License ---
Part of mde: a Modular D game-oriented Engine
Copyright © 2007-2008 Diggory Hardy

This program is free software: you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation, either
version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.



--- Introduction ---
This is a collection of all coding policies for the mde engine as a whole. Policies for individual packages should be put in the individual package directory or elsewhere.

These are principles, not cast-iron rules, which I (Diggory aka Cyborg16) generally try to adhere to. If any other programmers have better principles to apply over these rules, they can use them.

A warning: I have several times changed my mind about items I've written here. So don't expect all existing code to conform, and if you feel that a principle listed is not a good idea don't think you have to conform to it.



+++ CONTENTS +++

0   Introduction

1   Coding conventions

2   Documentation

3   Package design principle

4   Initialisation / cleanup

5   Testing

6   Logging

7   Exceptions

8   Translating strings

9 Floating point types 




--- Coding conventions ---
Mostly stick to those provided in the D specification.

Indentation: Preferably indent with four spaces and align comments either with tabs or spaces. If you do use tabs, use a tab-width of 8.

Long lines: Aim to break long lines at around 80 chars (particularly with documentation); this isn't essential but provides a good guide and keeps text looking reasonable. With code, however, breaking lines doesn't always produce better-looking code so just try to keep it neat. It used to be 100 chars.

Identifiers: as for the D spec, use descriptive words for identifiers, although try to keep them from being overlong. Use capital letters to show separate words, not underscores. E.g.: file, readFile; not: rdFile, read_file, readFileUsingMyMethodNow. Use CAPS for consts (with underscores as word separators), Capitalisation for class/interface/struct/union names, smallLetters for variables and class instances. Don't capitalise acronyms like SDL; instead use capitalisation like outlined above (personally I hate doing so but it does make things clearer).

Module/file names: If the module corresponds directly to a class (possibly also with some associated classes), use the class name with correct Capitalisation as the module name. Otherwise, use lower case. Always use the correct case when importing a module to keep code portable.

Spelling (within code): I am not going to stipulate British/American/other spellings, but keep to good English and use some consistancy (e.g. don't use both "defense" and "instance" together, unless unavoidable due to existing code).



--- Documentation ---
Documentation falls in to the following four categories:

Source file comments: Code comments only aimed at yourself or other programmers.

Source file DDoc: Documentation of the general working of modules, function purposes, syntaxes and operations, enums, classes, etc. Often only for public items. Also mostly only relevant to programmers.

codeDoc directory: For large amounts of documentation on the general working/principle/whatever of a module/package or mde in general. Directory hierarchy should mirror that of the source directory and files should have appropriate names and extensions (.txt if plain text). The most important difference between the codeDoc and doc directories is that information in codeDoc is only aimed at programmers, wheras information in doc is aimed at all users.

doc directory: Documentation aimed at the end user covering building, running, modding (excluding by programming), etc. Files should be appropriately named with an appropriate extension (.txt for plain text). Documentation regarding modding should be in a "modding" subdirectory. Documentation regarding translating may also be added.



--- Package design principle ---
Use a separate package for each module of the engine. In most packages where there is only one module (file) imported by other parts of the engine, that module should have the same name as the package and be designed to have a standardised interface to the package so that the package could be replaced with another as a drop-in replacement (written with the same interface). Of course in many cases it may not be possible to swich one package for another quite this easily, but holding to this principle should at least minimise the amount of work necessary when doing so.


Module imports:
Only publically import module A into module B if you can be fairly sure that any module C importing module B will also need module A.

Try to make it obvious which module the symbols you use come from. Sometimes using static or renamed imports is the best way to do this. Sometimes selective imports also help avoid confusion.

Where it is not obvious, show what the import is used for with a comment or selective import showing which symbols are used.

Chain dependancies: Avoid chain dependancies between modules (e.g. use interfaces). Chain dependancies can cause many problems.



--- Initialisation / cleanup ---
This can be done in two ways:

mde.Init: This class is designed to handle all but the simplist startup and cleanup code, and thus allow it to be threaded where appropriate and allow user feedback of loading progress.

static [~]this(): Use of static CTORs/DTORs should be limited to very small operations which can definitely be run safely at this stage.



--- Testing ---
Testing should, as far as reasonably possible, be done by unittests, defined either in the appropriate module or another module. Any modules containing unittests must be imported by test.mdeTest.

Unittests must be wrapped in "debug(mdeUnitTest)" statements. (These may also be used to wrap imports and "static this()" only needed by the unittest.)

Unittests (last block in the module where multiple unittest blocks are used) should end with:
    logger.info ("Unittest complete.");
No more logging should be needed, since if it fails whoever runs the unittest will know about it, and logging messages cannot be used to tell how complete the unittesting is. These messages just confirm that the unittests ran really.



--- Logging ---
Logging should be handled by tango's Logger class. A logger with the name of the form mde.package.module or mde.package.module.X where X is a symbol within the module should be used for each module.

In general the levels should be used as follows:
	Trace	Where desired for debugging, and only compiled in debugging mode (i.e. wrap with debug).
	Info	Sparingly, for informational purposes. Should not be used repetitively (within loops, etc.). Not for reporting unexpected behaviour or tracing program's running, except to the degree an end user may want. Also for unittests. Temporarily for reporting information to the user, until the GUI can be used instead.
	Warn	For when something unexpected happens which is not necessarily an error (although could be).
	Error	For anything which is definitely an error but not fatal.
	Fatal	For errors directly (i.e. definitely) ending the program.

For all levels except trace, messages should if possible be understandable to end users, while (for warn and above) including enough information to fix the problem when it is due to data files rather than code.
Thus:
	Trace output should only be available when compiled in debug mode.
	When run by an end-user (with info-level logging enabled),
	• info messages normally occur and should be understandable to end users;
	• warn messages may occur, and may indicate problems;
	• error messages indicate that something is definitely wrong (even if it only has a minor effect on the program as a whole);
	• fatal messages indicate a problem preventing the program from running.

Log/exception messages can be divided into two categories: those aimed at end users or modders and those only aimed at developers. A short string, either a brief English message or just a code, should be defined in the code, which can either be translated to a full message by I18nTranslation or output directly. The code string need not be a long description since it can be looked up in the code.



--- Exceptions ---
Exceptions should only be used for errors (see comment on log levels above). Thus when an exception is caught, by definition an error occured.

Thrown exceptions should, where documented, be documented via the logger.

Thrown exceptions should use an exception class specific to at least the package involved to enable specific catching of errors. Exception classes should be defined within a module exception.d in the package directory. Exception classes should generally follow the conventions within mde/exception.d to aid in providing reasonable error messages.



--- Translating strings ---
User output (internationalization support): i18n.I18nTranslation is designed to fetch a full string and optionally an associated descripion given an identifier. The identifier may also be a code symbol, and should be brief, in English, and give at least some idea of its meaning, since if no translation is available the identifier will be output. For example:  "example message" or "exampleMessage", not "An example of a message". For any new entry created, at least one full entry should be added to the i18n database for some form of English so that:
    (a) there is something available to translate to other locales
    (b) English developers can understand what it means



--- Floating point types ---
The issue is whether to use float, double or real as the basic floating point type. In some cases which types are appropriate for use is set by the prototypes of external methods being used. In other cases, which type to use is left entirely to the user's discretion. Since even modern 32-bit CPUs support native 80-bit floating point operations, there seems little point in using floats. Since doubles are more widely supported than reals and provide sufficient precision for most purposes, double seems to be the ideal type to use. Actually some basic tests with 32-bit dmd on my athlon-64 (under 64-bit linux) show floats to be slower to use than doubles but faster than reals, so this appears to be a good choice for now.