== Directories == === Library only === * Put library files in .../components/ * Put unit test files in .../components//test * Put additional, inessential programs (such as runtime tools to configure, dump, or query data in the library) in: .../components//bin * Generate libcl.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/ * Put unit test files in .../components//test * Put server files in .../components//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//bin * Put files common to client library and server in .../components//common But common files still get linked into libcl.so and .a * Generate libcl.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 clApi.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 clIpi.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: {{{#!highlight cpp namespace SAFplus { class Checkpoint // Class { ... }; typedef enum // Enum { LOG_SEV_EMERGENCY = 0x1, ... }; Logger* logInitialize(void); // A function int logSeverity; // A Variable }; }}} * All internal APIs must be defined under the ''SAFplusI'' namespace (I for internal): {{{#!highlight cpp namespace SAFplusI { }; }}} * 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 = ; 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 {{{#!highlight cpp namespace SAFplusI { class InternalObject { ... }; }; }}} clSvcApi.hxx {{{#!highlight cpp #include namespace SAFplus { class Svc { ... InternalObject& getIo(char* key); // WRONG: InternalObject is exposed to the application so should be in the API, not IPI. protected: InternalObject array[20]; // CORRECT: InternalObject is not intended to be used by applications. }; }; }}} Note 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. ''Example:'' '''''With pointers, predeclarations are preferred''''' {{{#!highlight cpp namespace SAFplusI { class InternalObject; // Predeclaration }; namespaces SAFplus { class Svc { ... protected: InternalObject* array; }; }; }}} * 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. * 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: {{{#!highlight cpp class Buffer { uint32_t refAndLen; // 8 bits (highest) reference count and 24 bits length combined into one. 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 }; ... const Buffer& SAFplus::Checkpoint::read (const char* key) const { size_t len = strlen(key)+1; // +1 b/c I'm going to take the /0 so Buffers can be manipulated as strings char data[sizeof(Buffer)-1+len]; // -1 because inside Buffer the data field is already length one. Buffer* b = new(data) Buffer(len); *b = key; return read(*b); } }}} 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. * 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''''' {{{#!highlight cpp if (foo) { a++; b--; } if (foo) { a++; b--; } }}} '''''NO''''' {{{#!highlight cpp if (foo) { a++; b--; } }}} * Parenthesize rather then rely on order of operations. '''''YES''''' {{{#!highlight cpp if ((foo && ((a == 1) || (b==2))) & !a) }}} '''''NO''''' {{{#!highlight cpp if (foo && a == 1 || b==2 & !a) { }}} === 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''''' {{{#!highlight cpp void copyObject(Object* dest, const Object& src); }}}