File: kambi_coding_conventions.txt

package info (click to toggle)
castle-game-engine 5.2.0-3
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 185,428 kB
  • sloc: pascal: 260,781; cpp: 1,363; objc: 713; makefile: 537; xml: 496; sh: 480; php: 4
file content (137 lines) | stat: -rw-r--r-- 6,265 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
This file describes some of Michalis Kamburelis'
(aka Kambi) Pascal coding conventions.
More overall overview of my units is in "Introduction" section
of documentation, see
[http://castle-engine.sourceforge.net/apidoc/html/].

- *.pas files are units, *.inc are files to be included in other Pascal
  source files using $Include.

  *.lpr are main program files.

  Previously I used *.pasprogram extension (to signal that my programs
  don't actually require Lazarus or LCL), but it was too non-standard,
  and thus making more trouble than it was worth.
  Some people use *.pp for units
  and *.pas to indicate main program file, but that's not a clear
  thing (especially from Delphi background, where units are *.pas).
  Delphi people use *.dpr, but that's Delphi, and my programs
  are for FPC.

- Whenever I was modifying Pascal sources of someone else
  I was marking my modifications by word "Kambi" somewhere in comments.
  "Kambi+" means that I added something,
  "Kambi-" that I commented out something,
  "Kambi*" that I changed something.

- Reentrant routines:

  All routines in my units, just like in any other sane library
  in any programming language, try to be reentrant.
  Functions that can't be reentrant are clearly marked in documentation
  as NON-REENTRANT.

  Generally, reentrant routine is one that is safe to be called recursively
  (e.g. it's implementation may be recursive, or, if reentrant routine
  X receives a pointer to routine Y, then Y may call X without any
  problems; e.g. in TFileProc passed to EnumFiles you can call EnumFiles again)
  and it's safe to use in multi-threaded programs.

  Routine is reentrant when
  - it does not write any static (global or within a unit's implementation)
    variables and
  - it calls only other reentrant routines

- Some naming conventions:

  - If some procedure modifies it's 1st parameter then I usually
    end it's name with "To1st" ("to first", i.e. "returns result back
    to first parameter").

    Often you will be able to see the same function coming in two
    flavours:
      F(const X: <type>, ...):<type>
      FTo1st(var X: <type>,...)
    The 1st (functional-like) version is more flexible,
    but the 2nd version may be faster (especially if type <type> is large,
    or requires time-consuming initialization).

    See e.g. CastleVectors and CastleImages units.

    Although I must admit that often I don't keep this convention,
    especially when <type> is some object class.
    Well, you very seldom should write functions that take an object
    reference and modify it somehow. After all, usually you should
    rather make new constructors and destructors for this class
    in such cases.

  - If somewhere I use parameters like V: ^<type> and Stride: Integer
    then it means that these parameters define a table of <type> values.
    Address of 1st item is V, address of i-th is (V + i * Stride).
    This is a convention used by OpenGL's vertex array routines.

    Stride may be negative. Stride may also be 0, then it means
    that Stride = SizeOf(<type>).

- Compilation symbols used:

  Besides standard FPC, GPC and Delphi ones (like MSWINDOWS, UNIX, LINUX,
  CPUI386, CPUX86_64 to differentiate between compilation platforms,
  FPC and __GPC__ to differentiate between compiler versions).

  Since 2007-10-12, I use MSWINDOWS symbol to designate both 32 bit and 64 bit
  Windows. WIN32 symbol is only for 32 bit Windows.
  FPC >= 2.2.0 defines MSWINDOWS as it should, so there's no problem.

  I also use DEBUG symbol in my sources, with obvious meaning.
  See castle-fpc.cfg for it's exact meaning. There's also RELEASE symbol,
  but I usually don't check for it's existence -- the logic says that
  $ifdef DEBUG then I'm in debug mode, else I'm in release mode.
  So there's no need to check for RELEASE symbol.

- Exceptions' messages:
  - Never start them with 'Error: ' or 'Error - ' or anything else
    that just says "we have an error". Reason: you're raising an Exception,
    this already says that this is some kind of error.
  - Don't end the Message with '!' character. Yes, I know that some
    exceptions mean that something really horrible happened,
    but screaming about this will not help -- let's keep cold blood :)
  - Don't end the Message with '.' character.
  - Usually Message should be a single sentence.
  - Message should not contain any line-breaks. Reason: this doesn't
    look good when displayed in some situations. Especially when
    one Message is embedded as part of the Message of other exception.
    But, if Message really must express line-breaks: they should be
    always expressed as CastleUtils.nl constant.
  - Message must not contain any general program information like
    ProgramName, ExeName etc. (The exception to this rule is when
    such information is really related to the error that happened,
    may help to explain this error etc.)
    In normal situation, the code that finally catched and outputs
    this exception should show such information.

- Callbacks. In the hybrid language like ObjectPascal, there's
  always a problem when I want my procedure to take a pointer to another
  procedure.
  Should I make this "a pointer to a method" or "a pointer to a procedure" ?
  (they are binary different, since "a pointer to a method"
  is actually two pointers (to object instance and to method code))
  Should I add some generic "Data" parameter ?

  Experience seems to show that generally callbacks should be not "of object",
  and should have "Data: Pointer" parameter. Advantages:
  - It's always straightforward to make a wrapper that uses the "Data"
    pointer to pass object's instance, so transforming to "of object"
    kind of callback is always possible.
  - At the same time, when you have some purely procedural code, you can always
    easily use this callback (without the need to declare some dummy class
    just to hold your callback in, or do dirty tricks to treat normal procedure
    as a method)

- Note that for now most parts of my code generally can't handle
  MBCS (multi byte character set) strings. Contributions are welcome,
  if someone needs it.

Michalis Kamburelis (aka Kambi)
<michalis.kambi@gmail.com>
http://castle-engine.sourceforge.net/