Right | FooKBGetInfoFree() |
Avoid | FOO_KB_GET_INFO_FREE() |
Avoid | FOO_kb_get_info_free() |
Avoid | FOO_kbgetinfofree() |
Right | if (foo) bar(foo); |
Wrong | if (foo) bar(foo); |
Avoid | foo && bar(foo); |
Right | if (foo) { bar(foo); ++gik; } else { mumble(foo); --gik; } |
Wrong | if (foo) { bar(foo); ++gik; } else { mumble(foo); --gik; } |
Wrong | if (foo) { bar(foo); ++gik; } else { mumble(foo); --gik; } |
OK | if (foo) { bar(foo); ++gik; } else { mumble(foo); --gik; } |
Avoid | if (foo) { bar(foo); ++gik; } else { mumble(foo); --gik; } |
Right | f = (c * 9 / 5) + 32; |
OK | f = 32 + c * 9 / 5; |
Avoid | f=32+c*9/5; |
Right | foo = bar(dog, wombat, bear, fox, ferret, deer, squirrel); for (i = 0, j = 0; i < num; ++i) j += a[i]; |
Wrong | foo = bar(dog,wombat,bear,fox,ferret,deer,squirrel); for (i = 0,j = 0;i < num;++i) j += a[i]; |
Right | foo = (thing *) bar; |
Wrong | foo = (thing*) bar; |
Wrong | foo = (thing*)bar; |
Avoid | foo = (thing *)bar; |
Right | a = (b + c); mumble(q); |
Wrong | a = ( b + c ); mumble( q ); |
Wrong | a = ( b+c ); mumble (q); |
Right | Cdef1(void, DoIt, void *, ptr) { mumble a = (mumble) ptr; boink b; splat c; if (!a) return; b = a->weep; c = a->drip; ... } |
Wrong | Cdef1(void, DoIt, void *, ptr) { mumble a = (mumble) ptr; boink b = a->weep; splat c = a->drip; if (!a) return; ... } |
Right | thingy *foo; thingy *bar; if ((unsigned char huge *) foo < (UInt1 huge *) bar) mumble(foo, bar); |
Wrong | thingy *foo; thingy *bar; if (foo < bar) mumble(foo, bar); |
Wrong | thingy *foo; thingy *bar; if ((unsigned long) foo < (unsigned long) bar) mumble(foo, bar); |
myheader.h | #ifndef MYHEADER_PROTECT #define MYHEADER_PROTECT typedef short myInt2; #endif /* !MYHEADER_PROTECT */ |
foo.c | #ifndef MYHEADER_PROTECT # include "myheader.h" #endif int main(int argc, char **argv) { ... } |
exit(1)
. Control structures look
like while (1)
. Notice the space.
#define BAR_lsv(tag) (((BAR_LSV
*)(global->gen))->tag)
, which is also confusing because
global
is not an argument of the macro.
indent-tabs-mode
to nil
is a good idea.
int *i;
the asterisk binds to
the i
, meaning "i
is a pointer to
int
." Avoid writing int* i;
because
what it implies contradicts how the parser sees it. After all,
we don't declare an array with int[10] i;
while
of a
do...while.
/* * this is some text * about something */should be avoided becuase it is quite time-consuming to keep the asterisks vertically aligned after making significant changes.
((foo != bar) ? foo :
-1)
. We're not all MIT Lisp hackers who love to see
(isusr ? (exptopic ? MSG_TCMP_UEXPORT1 : MSG_TCMP_UEXPORT0)
: (exptopic ? MSG_TCMP_FEXPORT1 : MSG_TCMP_FEXPORT0))
in
code.
typedef struct _XXX { ... } XXXRec, *XXX;Macros should also begin with their package name. Symbolic names for values of a certain type should be named <type>_<name>, as in FooQueryWeight_Nice. Other macros should probably follow the tradition of all uppercase.
class
,
public
, protected
,
private
, virtual
, friend
,
inline
, this
, operator
,
new
, delete
, catch
,
throw
, try
, template
,
bool
, false
, and
true
.
const
when you can. This will allow the
compiler to find errors for you. This is especially important
when writing functions for general use. Declare arguments
(especially pointers) as const
if possible. This
allows clients to use functions without casting away const.
Runs on all/many machines
A types.h
sort of file with ifdef'd typedefs will
accomplish this quite well.
For total interchangability, each unit should define and export all of the datatypes it uses. Clients would then cast everything back and forth. Disappointingly, many compilers won't enforce that types have the same name. Also, casts tell compilers to stop checking types, thus removing the safety net. There still must be some conventions, like that strings are null-terminated ASCII, regardless of signedness.
Some interchangeability can be traded for convenience if a group (e.g.: a company) decides to use one set of data types throughout all of its code. This corpus of code will now depend on the .h file defining the data types. Someone will need to decide if this .h file gets shipped to any customers or is used in customer interfaces.
A company may decide to have one set of portable datatypes defined for internal use and a different set of datatypes for customers.
Macros can "genericize" code, massaging away differences between compilers and dialects. These macros should be separate from data-type macros.
The vast majority of code should be platform-independent. Differences should be abstracted and the implementations should be ifdef'd for each platform. People doing ports should be able to identify the platform-specific files and change only them in order to complete the port.
It may be a good idea to reserve a portion of the filename/symbol namespace for OSD code so that OSD files/symbols are easily identified and cannot conflict with generic files/symbols.
All platforms should be able to read and write the same files and talk on the network to other platforms. Conventions for big-endian vs. little-endian, etc. should be set up.
With internationalization, text-based control files might become an issue.
Proper namespace management yields two benefits: partitioning and documentation. Partitioning guarantees that external symbols in one module will not collide with those in another. This is important since it's hard to predict what pieces of code will need to interoperate. Usually partitioning is accomplished via prefix assignment. Documentation is a side-effect of intelligent partitioning. It means that a developer can instantly tell from the name of a function, datatype, or variable what it is and what part of the system it occupies.
File names should correspond to symbol names defined within. Symbols private to the file should start with lowercase.
Does something another program might need.
Proper documentation and "advertising" is essential to keep others from reimplementing.
Specific functionality provided by client.
One module doesn't drag in five others
Use encapsulation, client-supplied callbacks, and lazy initialization.
Instead of relying on MUMBLE_alloc() for memory, a package can take a pointer to an alloc function at initialization time and then be flexible enough to work with malloc() too.
This is probably to most important aspect of code, and the most difficult to teach or codify.
Can be understood and debugged.