The developers of the MrSID SDK follow a general set of coding conventions and guidelines. While we certainly don't expect third-party developers to adhere to these conventions, they are described here as an aid to understanding and working with the SDK, both at the syntactic and semantic level.
Note that these are only conventions, not hard rules; the SDK does not follow all of these guidelines all the time, for reasons of practicality, historical practice, compatibility, etc. Where important for developers, such deviations are noted in the Reference Manual.
Line length
Lines should be no longer than 78 columns in order to avoid line- and wordwrapping.
Tabbing
Tabs are implemented as 3 spaces.
Namespaces
Header files should surround declarations with LT_BEGIN_NAMESPACE(LizardTech) and LT_END_NAMESPACE(LizardTech), and source files should declare LT_USE_NAMESPACE(LizardTech) at the top.
Warning levels
Source files should be compiled with high warning levels on Win32 compilers:
#if defined(LT_COMPILER_MS)
#pragma warning(push,4)
#endif
...
#if defined(LT_COMPILER_MS)
#pragma warning(ppop)
#endif
Variable names, type names, etc
- variables are spelled using camelcase: upperLeftPos
- function names are also spelled using camelcase: getUpperLeft()
- class member variables use an "m_" prefix: m_xPosition
- static variables (whether class members or not) use an "s_" prefix: s_twoPi
- enum and type names are capitalized: LTIImageStage, LTIDataType
- however, the integral primitive types are an exception: lt_uint8
- enum values are uppercase and generally are named to reflect their datatype: LTI_DATATYPE_UINT8
- macros are uppercase: LT_USE_NAMESPACE
Integral primitives
The use of the typedef'd primitive datatypes, e.g. lt_uint8, is preferred when possible and practical.
File names
For the "LT" and "LTI" classes, the source filenames are spelled with leading prefixes – for example, class LTIGeoCoord is defined in header lti_geoCoord.h. For all other classes, the filename matches the class name – for example, class MG4ImageWriter and header MG4ImageWriter.h.
Initialization
As a precaution, all variables should be initialized at the point of declaration. A value of 0 or -1, INVALID (available for most enums), or LT_STS_Uninit (for status codes) is usually appropriate.
Status codes
The public SDK does not use exceptions, and the internal implementation uses them only sparingly. In general, functions should return status codes rather than relying on throw. While putting an additional burden on the user, our experience has shown that status codes are both more portable and less prone to error than the alternatives. If a function returns a status code, always check it. The standard idiom for checking status codes is:
LT_STATUS
Class::foo()
{
...
LT_STATUS sts = bar();
if (!LT_SUCCESS(sts)) return sts;
...
return LT_STS_Success;
}
When writing a new function, consider returning an LT_STATUS status code hardwired to LT_STS_Success instead of just returning void. If future development of the function requires the ability to return failure codes, downstream uses will not have to be changed.
Constructors
"Heavy" objects which require nontrivial work in their constructors should use an initialize() function. This requires extra code for the user, but provides a means for returning status codes back to the user without relying on exceptions. In particular, objects should not (explicitly or implicitly) call new or any other nontrivial function from within their constructors.
The "creator-deletes" rule
The object that creates a new object is considered to be the "owner" of that new object and as such has responsibility for deleting it. Passing the allocated object by address to a function doesn't pass ownership to that function (unless documented otherwise).
Reference counting
The SDK uses reference counting to manage the lifespan of objects that comprise image pipelines. This is similar in spirit to “smart” or “auto” pointers. The static member function create() of a class should be used to create a new instance of that class. Similarly, the (non-static) member function release() should be used when you are done with object – for example, when the pointer to the object goes out of scope. The retain()function should be used to hold onto an object, i.e. increment the reference count; if you created the object, however, do not call retain(). When the last reference calls release(), the object will delete itself. The two most common reference counted classes are LTImageStage and LTIImageStageManager.
Overrides
The SDK uses a system of “mixins” to simplify the overriding of LTIImageStage properties. Specifically, LTIImageStage defines an abstract interface that needs to be implemented by derived class of LTIImageFilter; LTIImageFilter implements the LTIImageStage interface by forwarding the method call to the next LTIImageStage in the pipeline. Derived classes of LTIImageFilter that change image properties will need to override the accessor functions.
Pass-by-reference
Data should be passed by reference whenever possible, for example LTIImageStage& rather than LTIImageStage*. Pass-by-address is preferred when reassigning ownership or when NULL is being used for some sentinel value.
Const
Use const whenever possible. In function declarations, it is used to indicate "in/out" semantics for parameters (excluding by-value parameters). In variable declarations, it is used to clarify intent of the variable's usage.
Disabled standard class member functions
Unless they are required by the class, it is preferable to explicitly disable the assignment operator, copy constructor, and default constructor.
private: LTINavigator(); LTINavigator(LTINavigator&); LTINavigator& operator=(const LTINavigator&);
One class per header file
A header file should contain the declaration of only one class. Implementations should not be in the header file unless truly warranted.
Inlining
Implementations should not be defined inline in the header file unless truly warranted. Do not use compiler-specific inlining pragmas.
Templates
Use templates sparingly and only when truly warranted, as they lead to code bloat and/or incompatibilities among various compilers. Consider using a templated function private to a source module instead of making a public templated class. If you must use templates, avoid the more complex parts of the language.
Template headers
A header file containing only a templated class should have a corresponding source file which includes it, even though the source file contains no implementation code.
Global data
The SDK contains no global variables. No non-const module statics or singleton classes are used.
Threadsafety
The SDK may be safely used in multithreaded applications. Locking of SDK objects is NOT provided, however; the application must guarantee each object is accessed serially within a particular thread context.
Assertions
Assertion macros, specifically
LT_ASSERT()
, should be used liberally.
Delegates
Where appropriate, for "callback" or "handler" mechanisms the use of simple abstract classes ("delegates") is preferred to using a function pointer typedef.
Set/get
Use set()/get() style member variable access instead of making data members public.
STL
Do not use STL in public interfaces, as this may cause linkage problems. While the SDK uses STL internally in some areas, its use is in general discouraged.
Optimization
Do not write "hand-optimized" code. Write first for correctness and maintainability; optimize it later only if profiling justifies it.