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
- *.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
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)
- 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
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
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)