Directories
Library only
- Put library files in
../components/<library name>
- Put unit test files in
../components/<library name>/test
- Put additional, inessential programs (such as runtime tools to configure, dump, or query data in the library) in:
../components/<library name>/bin
Generate libcl<library name>.so and .a
Put ALL AND ONLY ALL library .o files in $(MWOBJ_DIR) for eventual link into libmw.so & libmw.a
- Put non-library .o files in $(OBJ_DIR)
Client/Server
- Put client library files in
../components/<library name>
- Put unit test files in
../components/<library name>/test
- Put server files in
../components/<library name>/server
- Put additional, inessential programs (such as runtime tools to configure, dump, or query data in the library or related shared memory segment) in:
../components/<library name>/bin
- Put files common to client library and server in
../components/<library name>/common But common files still get linked into libcl<library name>.so and .a
Generate libcl<library name>.so and .a
Put all and ONLY ALL library .o files in $(MWOBJ_DIR) for eventual link into libmw.so & libmw.a
- Put non-library .o files in $(OBJ_DIR)
Files
- Use .hxx and .cxx and prefix all header files with "cl".
example: clTransaction.hxx, transaction.cxx
- Use bumpyCase for file names, with first character lowercase.
Name major functional blocks cl<FN>Api.hxx
examples: clLogApi.hxx, clCkptApi.hxx
An application only needs to include these files. These files must include any other required header files.
- Other files can be named based on the primary class defined inside
examples: clTransaction.hxx
An application MAY include these files if it wants that specific functionality.
Internal APIs should be named cl<something>Ipi.hxx
examples: clCkptIpi.hxx
These files may get pulled into application builds due to object containment. However, it must not be necessary for the application program to ever use symbols defined in the Ipi files.
Definitions
All application-level APIs must be defined under the SAFplus namespace:
All internal APIs must be defined under the SAFplusI namespace (I for internal):
- Do not use #define because preprocessor symbols cannot go under a namespace
Exceptions: macros like shorthand versions of functions that ask for FILE and LINE use enums or const <type> <variable> = <value>; Exceptions must be prefixed with "cl" -- example: #define clMyMacro(x)
- Do not hide implementation using handles.
- Expose them via pointers or containment. This makes debugging much simpler and its easy enough just to skip over tracing through the implementation if needed. Also, prefer containment; do not use pointers rather than containment just to hide implementation.
Example: clSvcIpi.hxx
clSvcApi.hxxNote that in this case, the application will end up including the Ipi file. This is ok, it is clear by the namespace what is API and what is internal.1 #include <clSvcIpi.hxx> 2 namespace SAFplus 3 { 4 class Svc 5 { 6 ... 7 InternalObject& getIo(char* key); // WRONG: InternalObject is exposed to the application so should be in the API, not IPI. 8 9 protected: 10 InternalObject array[20]; // CORRECT: InternalObject is not intended to be used by applications. 11 }; 12 };
Example: With pointers, predeclarations are preferred
- Expose them via pointers or containment. This makes debugging much simpler and its easy enough just to skip over tracing through the implementation if needed. Also, prefer containment; do not use pointers rather than containment just to hide implementation.
- Do not use "cl" prefix for class or variable declarations.
- A prefix is unnecessary because we are under the SAFplus namespace.
- Classes should be defined using bumpy case, with a leading Capitalization.
Example: MyClass, Checkpoint,
- Variables should be defined using bumpy case with no leading capitalization. Do not use underscores.
Example: myVariable, thisIsAVariable,
- Prefer enum classes over enums over #defines
- Enums are under the SAFplus namespace, macros are not. Exception: Bit fields should use enums (not enum classes) because enum classes are meant to define entities that have no numerical relationship (like RED, GRN, BLUE). #defines cannot be put under namespaces so are particularly bad to use.
- Prefer inline functions over macros
- This is important because macros cannot be put under namespaces. Therefore macros (#define) must use the "classic" naming style (CL_ prefix).
A common exception to this is when you need FILE and LINE macros.
- This is important because macros cannot be put under namespaces. Therefore macros (#define) must use the "classic" naming style (CL_ prefix).
- Constants should be all caps with underscores
Example: LOG_SEV_EMERGENCY Constants can be actual enum constants, or other items that you want to appear constant to applications even if they are not.
Example: const WellKnownHandle APP_LOG(2,0);
- Do not use "using namespace xxxx;" inside a header file.
- Doing so forces every cxx file that includes this header to be 'using' that namespace and therefore defeats the purpose of having 2 namespaces.
Code
When you write code, you are not primarily communicating to the computer. You are communicating an algorithm specification to the next engineer who will be looking at your code. Write your code so an engineer with < 5 years of experience can understand it!
- Line length
- Forget about 80, 120, etc character line lengths. Modern, widescreen monitors are much longer than they are tall. Put at least one statement on each line. And in extremely rare cases you might put 2 statements on one line if they form a conceptual unit.
- Comment your code!
- Expect that the reader knows c++ so do not explain what something does. Answer the question "Why". Example:
1 class Buffer 2 { 3 uint32_t refAndLen; // 8 bits (highest) reference count and 24 bits length combined into one. 4 char data[1]; // Not really length 1, this structure will be malloced and placed on a much larger buffer so data is actually len() long 5 }; 6 7 ... 8 9 const Buffer& SAFplus::Checkpoint::read (const char* key) const 10 { 11 size_t len = strlen(key)+1; // +1 b/c I'm going to take the /0 so Buffers can be manipulated as strings 12 char data[sizeof(Buffer)-1+len]; // -1 because inside Buffer the data field is already length one. 13 Buffer* b = new(data) Buffer(len); 14 *b = key; 15 return read(*b); 16 }
- Also note in the buffer allocation case I did not hand-optimize the +1 and -1, by removing them. With a 2.4Ghz machine, this is a useless optimization and obfuscates the code.
- Expect that the reader knows c++ so do not explain what something does. Answer the question "Why". Example:
- Do not use goto
- Better to not malloc/free dynamically (allocations are expensive) but if you must do so use scope objects to handle automatic allocation and deallocation.
- line up your { vertically, except for single line stuff.
YES
NO
- Parenthesize rather then rely on order of operations.
Function definitions
- Prefer to return the value and throw an exception if there is an error (all exceptions should be derived from SAFplus::Error defined in clCommon.hxx)
- If returning multiple values, use std::pair et al.
- Use const references for objects that do not change (input parameters), and pointers for objects that do change (output parameters -- however, prefer to return output parameters)
YES
1 void copyObject(Object* dest, const Object& src);