Difference between revisions of "Coding standard"

From Second Life Wiki
Jump to navigation Jump to search
(Fix licensing guidelines (oops...))
m
 
(64 intermediate revisions by 14 users not shown)
Line 1: Line 1:
{{OSWikiLearnBox}}
{{OSWikiLearnBox}}
{{TOCright}}
 
== Linden Lab Coding Standard ==
This document defines and formalizes the style of code produced at Linden Lab.  Adherence to these conventions is critical to ensuring readable and maintainable code.
This document defines and formalizes the style of code produced at Linden Lab.  Adherence to these conventions is critical to ensuring readable and maintainable code.
Some tools are available for checking some parts of these rules in the [https://github.com/secondlife/git-hooks] repository.
__TOC__


== Common Guidelines ==
== Common Guidelines ==
=== Line Endings ===
=== Line Endings ===
If you are checking in a new text or text-like file (e.g. XML, C++ source, etc) under Subversion, you must tell Subversion what line ending policy to use.


svn propset svn:eol-style native myfile.cpp
 
All text files '''must''' use unix (linefeed only) line endings when submitted.
 
Exceptions are allowed ''only'' if the file in question:
* Is used or relevant ''only'' on Windows
* Requires DOS style (carriage-return linefeed) line endings for correctness.
 
Files that have DOS line endings must either:
* have a suffix that is one of:
** .bat
** .sln
** .vcxproj
* '''or''' be located somewhere under a directory named <tt>windows</tt> or <tt>vstool</tt>
 
See [[How to avoid DOS line endings in Windows tools]]
 
==== Trailing Whitespace ====
 
Trailing whitespace must be trimmed from the ends of lines. Most text editors have a way to do this automatically.


=== Comments ===
=== Comments ===
When commenting, address the "why" and not the "what" unless the what is difficult to see. If what the code requires commenting, also include why it has to be done that way so maintainers can follow your reasoning. Use complete sentences and correct spelling.
When commenting, address the "why" and not the "what" unless the what is difficult to see. If what the code does requires commenting, also include why it has to be done that way so maintainers can follow your reasoning. Use complete sentences and correct spelling.


Prefer the use of [http://www.doxygen.org/ doxygen] compatible comment markup. (See [[Using Doxygen]].)
Prefer the use of [http://www.doxygen.org/ doxygen] compatible comment markup. (See [[Using Doxygen]].)


Sometimes you know something is broken or a bad idea but need to do it anyway -- please follow the special comment token guidelines to not confuse the next engineer. Append your name or initials and the date of the comment when using these special comment tokens.
Sometimes you know something is broken or a bad idea but need to do it anyway &mdash; please follow the special comment token guidelines to not confuse the next engineer. Append your name or initials and the date of the comment when using these special comment tokens.


{| border="1"
{| border="1"
Line 22: Line 42:
|-
|-
| *FIX
| *FIX
| The associated code is actually broken and has known failure cases. All instances of these should be fixed before shipping. Do not use this tag for hacks or suggested development. Do not simply tag the code with the token alone or simply provide instructions since then it is unclear if that is how it is broken. For example: "// *FIX: invalidate stored textures when number of faces change" -- is really unclear if it is an instruction or a buggy side effect of the code segment.
| The associated code is actually broken and has known failure cases. All instances of these should be fixed before shipping. Do not use this tag for hacks or suggested development. Do not simply tag the code with the token alone or simply provide instructions since then it is unclear if that is how it is broken. For example: "// *FIX: invalidate stored textures when number of faces change" &mdash; is really unclear if it is an instruction or a buggy side effect of the code segment.
| FIXME BROKEN BUG  
| FIXME BROKEN BUG  
|-
|-
Line 42: Line 62:
|-
|-
||
||
<pre>// *FIX: This will fail if the packets arrive out of order.
<pre>
// *FIX: This will fail if the packets arrive out of order.


// *HACK: Filter out some extra information from this  
// *HACK: Filter out some extra information from this  
Line 54: Line 75:
// *NOTE: The tile is actually 257 pixels wide, so
// *NOTE: The tile is actually 257 pixels wide, so
// we have to fudge the texture coordinates a bit for this
// we have to fudge the texture coordinates a bit for this
// to look right.</pre>
// to look right.
</pre>
|}
|}


=== No New Serialization Formats ===
==== Move Away From LLSD ====
After much effort and unit-testing, LLSD is now the sanctioned format for all new serialization tasks. Do not implement another serialization scheme unless you have a signed and notarized letter from Elvis explicitly stating that a new serialization format is a good idea. If speed is essential, LLSD has a fairly speedy binary parser and formatter available in c++ and python.
When practical and prudent you should not use LLSD and should endeavor to remove it from code bases.
We have better things to do than pour time into maintaining and dealing with the consequences of a home-grown serialization format.
LLSD was okay in 200X, not 201X+. The obvious text-based alternative is JSON.


=== Unicode ===
=== Unicode ===
Line 64: Line 88:


=== File Names ===
=== File Names ===
File names should be significant and of reasonable length, with no spaces or uppercase letters.  Separate words in the name MUST be separated with underscore (like_this.cpp), because languages like Python cannot include module names with hyphens (will-not-import.py).
File names should be significant and of reasonable length, with no spaces or uppercase letters.  Separate words in the name MUST be separated with underscore (<code>like_this.cpp</code>), because languages like Python cannot include module names with hyphens (<code>will-not-import.py</code>).


=== Dead Code ===
=== Dead Code ===
Do not leave commented-out code in any commits that you make. Delete that code instead.
Do not leave commented-out code in any commits that you make. Delete that code instead.


Block-commented out code is especially problematic because it is not distinguishable from live code in a grep. Our version control systems are good enough that any code that was checked in is retrievable even after deletion.
Block-commented out code, including code disabled through #if <something-false> is especially problematic because it is not distinguishable from live code in a grep. Our version control systems are good enough that any code that was checked in is retrievable even after deletion.


== C++ ==
== C++ ==
Line 75: Line 99:
=== Source Files ===
=== Source Files ===
All C++ source code files should begin with the header shown below:
All C++ source code files should begin with the header shown below:
<pre>
<syntaxhighlight lang="cpp">
/**  
/**  
  * @file filename
  * @file file base name
  * @author Optional author field
  * @author optional
* @date Optional iso8601 creation date
  * @brief brief description of the file
  * @brief brief description of the file
  *
  *
  * $LicenseInfo:firstyear=2009&license=viewergpl$
  * $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  * $/LicenseInfo$
  * $/LicenseInfo$
  */
  */
</pre>
</syntaxhighlight>
The <em>$LicenseInfo$</em> section is critically important so that we can process the source files based on license agreements. If you're unsure, use viewergpl. The licenses we support are:
The <code>$LicenseInfo$</code> section is critically important so that we can process the source files based on license agreements. If you're unsure, use <code>viewerlgpl</code>. The licenses we support are:
* internal - something probably not meant for distribution unless we have an agreement with the third party.
* <code>internal</code> - something probably not meant for distribution unless we have an agreement with the third party.
* viewergpl - the viewer and linden libraries distributed with it
* <code>viewergpl</code> - '''deprecated''' this was used for pre-[[Project Snowstorm|Snowstorm]] viewer distribution
* mit - a basic mit license for some libraries such as mulib, eventlet, and indra.ipc.
* <code>viewerlgpl</code> - the viewer and linden libraries distributed with it
Replace the 2008 with the year the file is created. post-processing can convert the firstyear and license parameters into a binding copyright and license notice. The <i>linden/scripts/apply-license.py</i> script will do do that process on any collection of files passed on the command line.
* <code>mit</code> - a basic MIT license for some libraries such as mulib, eventlet, and indra.ipc.
The script <i>linden/scripts/insert-copyright.py</i> will create or prepend appropriate license and copyright information for python and c source files.  
Replace the <code>2011</code> with the year the file is created.


All C++ source files should end in a newline.  If not, GCC will fail with an error, as it enforces the C++ standard which requires one.
All C++ source files should end in a newline.  If not, GCC will fail with an error, as it enforces the C++ standard which requires one.


Headers should be included in the following order:
Headers should be included in the following order:
<pre>
<syntaxhighlight lang="cpp">
// All files MUST include one of the following first:  
// All files MUST include one of the following first:  
//  (includes linden_preprocessor.h, stdtypes.h, and some common headers)
//  (includes linden_preprocessor.h, stdtypes.h, and some common headers)
Line 108: Line 149:


#include <map> // stl headers
#include <map> // stl headers
#include <math.h> // std headers
#include <cmath> // std headers
#include "vorbis/ogg.h" // External library headers
#include "vorbis/ogg.h" // External library headers


#include "llbar.h" // Other linden headers
#include "llbar.h" // Other linden headers
</pre>
</syntaxhighlight>


==== File Names ====
==== File Names ====
All c++ source files should be prefixed with 'll'.
All C++ source files should be prefixed with 'll'.


File names must end with the appropriate suffix:
File names must end with the appropriate suffix:
* '''cpp''' for ANSI C++ code
* '''cpp''' for ANSI C++ code
* '''h''' for included declarations
* '''h''' for included declarations
* '''inl''' for large chunks of inlined c++ code. Avoid this.
* '''inl''' for large chunks of inlined C++ code. Avoid this.
* '''asm''' for assembly files. <em>Do not do this without a note from some deity forgiving your transgressions.</em>
* '''asm''' for assembly files. <em>Do not do this without a note from some deity forgiving your transgressions.</em>


==== Include Files ====
==== Include Files ====
Include files should be specified using relative paths using the '/' character to help ensure portability.   
Include files should be specified using relative paths using the '<code>/</code>' character to help ensure portability.   
In the code relative paths should not be used. Instead, paths should be indicated in the Makefile or VC++ project file.
In the code relative paths should not be used. Instead, paths should be indicated in the relevant CMake file.


Compiler directives used to prevent multiple header file inclusion should be based on the file name with a "LL_" preface.
Compiler directives used to prevent multiple header file inclusion should use the <code>#pragma once</code> preprocessor directive.
Include dependent headers in the header file, but use forward declarations where possible.
Include dependent headers in the header file, but use forward declarations where possible.


For example:
For example:
<pre>
<syntaxhighlight lang="cpp">
#ifndef LL_LLFOO_H
#pragma once
#define LL_LLFOO_H


#include "llbar.h"
#include "llbar.h"


class LLReferencedData; // forward declaration (no need to include "llrefrenceddata.h")
class LLReferencedData; // forward declaration (no need to include "llrefrenceddata.h")
// template forward declaration (no need to include "llotherreferenceddata.h" nor "llpointer.h")
class LLOtherReferencedData;
template<class Type> class LLPointer;


class LLFoo : public LLBar
class LLFoo : public LLBar
Line 143: Line 187:
public:
public:
     LLFoo();
     LLFoo();
     void setData(LLReferencedData& refdata);
     void setData(LLReferencedData& ref_data);
    void useOtherData(LLPointer<LLOtherReferencedData>& other_data);
private:
private:
     LLReferencedData* mRefData;
     LLReferencedData* mRefData;
};
};


#endif //LL_LLFOO_H
</syntaxhighlight>
</pre>


=== Basic Facilities ===
=== Basic Facilities ===
Line 165: Line 209:
** '''F64''' denotes a 64-bit floating point value ("double")
** '''F64''' denotes a 64-bit floating point value ("double")
* Other Types
* Other Types
** Prefer the use of the standard c++ '''bool'''. This is especially for comparators like operator< (using bool instead of BOOL will prevent lots of warnings when using STL). '''BOOL''' denotes a 32-bit integer value that is used to denote TRUE or FALSE. It is specifically not '''bool''' to prevent mistakes caused by "sizeof(bool)" being implementation specific.
** Prefer the use of the standard C++ '''bool'''. This is especially for comparators like operator< (using bool instead of BOOL will prevent lots of warnings when using STL). '''BOOL''' denotes a 32-bit integer value that is used to denote TRUE or FALSE. It is specifically not '''bool''' to prevent mistakes caused by "sizeof(bool)" being implementation specific.


==== Project Defines ====
==== Project Defines ====
* Use LL_WINDOWS, LL_LINUX, and LL_DARWIN for platform specific code.  
* Use <code>LL_WINDOWS</code>, <code>LL_LINUX</code>, and <code>LL_DARWIN</code> for platform specific code.  
* Use LL_RELEASE and LL_DEBUG for release and debug specific code.  
* Use <code>LL_RELEASE</code> and <code>LL_DEBUG</code> for release and debug specific code.  
* LL_RELEASE_FOR_DOWNLOAD and LL_RELEASE are both defined in versions destined to be in a resident's hands.
* <code>LL_RELEASE_FOR_DOWNLOAD</code> and <code>LL_RELEASE</code> are both defined in versions destined to be in a resident's hands.
* Prefer testing for the positive when using #if, and use #if/#if ! instead of #ifdef/#ifndef. For example:
* Prefer testing for the positive when using <code>#if</code>, and use <code>#if</code>/<code>#if !</code> instead of <code>#ifdef</code>/<code>#ifndef</code>. For example:
<pre>
<pre style="color:green">
#if LL_DEBUG && !LL_DARWIN
#if LL_DEBUG && !LL_DARWIN
</pre>
</pre>
instead of:
instead of:
<pre>
<pre style="color: red">
#ifndef LL_RELEASE && !defined(LL_DARWIN)
#ifndef LL_RELEASE && !defined(LL_DARWIN)
</pre>
</pre>
However, this construct is still useful:
However, this construct is still useful:
<pre>
<pre>
#ifndef TRUE
#ifndef SOMETHING
#define TRUE 1
#define SOMETHING SOMETHING_NEW
#endif
#endif
</pre>
</pre>
Line 190: Line 234:
See [[C++ Strings]] for more guidelines on strings in the C++ codebase.
See [[C++ Strings]] for more guidelines on strings in the C++ codebase.


Use std::string as a string container and std::ostringstream for constructing strings from multiple sources.
Use <code>std::string</code> as a string container and [https://github.com/secondlife/viewer/blob/main/indra/llcommon/stringize.h the STRINGIZE() macro] for constructing strings from multiple sources.


If you must use printf style formatting, use snprintf() or llformat(). Never use user supplied data (including configuration files) to specify formatting instructions to those functions.
If you must use <code>printf</code> style formatting, use <code>snprintf()</code> or <code>llformat()</code>. Never use user supplied data (including configuration files) to specify formatting instructions to those functions.


Avoid using c-string libraries. If you are writing something where speed is critical, prefer the use of std::vector<char> since it's usage is less error prone, you can explicitly allocate or occupy memory, it is easy to null terminate with a single call to container.push_back('\0'), and it is char* compatible with a call to &container[0].
Avoid using c-string libraries. If you are writing something where speed is critical, prefer the use of <code>std::vector<char></code> since it's usage is less error prone, you can explicitly allocate or occupy memory, it is easy to null terminate with a single call to <code>container.push_back('\0')</code>, and it is <code>char*</code> compatible with a call to <code>&container[0]</code>.
   
   
The following c functions are forbidden:
The following c functions are forbidden:
* sprintf() and vsprintf() -- most of the printf family of functions are like individual invitations to hackers to overflow a buffer. Don't use them.
* <code>sprintf()</code> and <code>vsprintf()</code> &mdash; most of the <code>printf</code> family of functions are like individual invitations to hackers to overflow a buffer. Don't use them.
* strcpy() and strncpy() -- both of these functions are dangerous and difficult to use correctly. For example, did you know that strncpy() does not null terminate if no null is found in the source?
* <code>strcpy()</code> and <code>strncpy()</code> &mdash; both of these functions are dangerous and difficult to use correctly. For example, did you know that <code>strncpy()</code> does not null terminate if no null is found in the source?
* strcat() and strncat() -- these methods are inefficient in comparison to using ostringstream() and are easy to misuse. The strncat() function is almost forgivable, but please just avoid these.
* <code>strcat()</code> and <code>strncat()</code> &mdash; these methods are inefficient in comparison to using <code>ostringstream()</code> and are easy to misuse. The <code>strncat()</code> function is almost forgivable, but please just avoid these.
* gets() -- Never, ever use this function.
* <code>gets()</code> &mdash; Never, ever use this function.


Our legacy being what it is, there are some unavoidable cases where we are forced to use c-string functions. Here are some tips:
Our legacy being what it is, there are some unavoidable cases where we are forced to use c-string functions. Here are some tips:
* sscanf(), fscanf(), etc... -- For these methods, always supply a maximum width for strings. If you do not know how to do this off the top of your head, do not use these functions. Even if you think you know, read the man page again.
* <code>sscanf()</code>, <code>fscanf()</code>, etc... &mdash; For these methods, always supply a maximum width for strings. If you do not know how to do this off the top of your head, do not use these functions. Even if you think you know, read the man page again.
* snprintf(), fprintf(), vsnprintf(), etc.. -- As noted above, never let anything other than the program determine the formatting. Never trust configuration files, assets, message system data, etc.
* <code>snprintf()</code>, <code>fprintf()</code>, <code>vsnprintf()</code>, etc.. &mdash; As noted above, never let anything other than the program determine the formatting. Never trust configuration files, assets, message system data, etc.


==== Unicode ====
==== Unicode ====
Useful functions for dealing with UTF-8 can be found in llstring.h. Remember that truncating a UTF-8 string must be done on glyph rather than byte boundaries or your string may no longer be valid UTF-8. Many methods in std::string and LLString are not UTF-8 safe since they work on sizeof(char_t) byte boundaries. Similarly, the functions in that header file which do not contain 'ut8f' in the prototype are probably not UTF-8 safe.
Useful functions for dealing with UTF-8 can be found in llstring.h. Remember that truncating a UTF-8 string must be done on glyph rather than byte boundaries or your string may no longer be valid UTF-8. Many methods in <code>std::string</code> and <code>LLString</code> are not UTF-8 safe since they work on <code>sizeof(char_t)</code> byte boundaries. Similarly, the functions in that header file which do not contain 'ut8f' in the prototype are probably not UTF-8 safe.


Do not use UTF-16 except with interfacing with Win32.
Do not use UTF-16 except with interfacing with Win32.


Avoid the use of wide strings except when iteration speed is essential, or you are interfacing with the host operating system.
Avoid the use of wide strings except when iteration speed is essential, or you are interfacing with the host operating system.
==== Trigraphs ====
A trigraph is a set of three characters that represents one character in the C character set. The set of trigraph sequences was defined in the ANSI Standard to allow users to use the full range of C characters, even if their keyboards do not implement the full C character set.
Each trigraph sequence is introduced by two question marks. The third character in the sequence indicates which character is being represented. The standard trigraphs and the characters they produce are:
??(  [
??/  \
??)  ]
??'  ^
??<  {
??!  |
??>  }
??-  ~
'''Using trigraphs anywhere in Second Life code is not allowed.'''


==== Containers ====
==== Containers ====
Prefer the use of the containers available in the c++ standard template library. They are well tested and in common usage across the industry -- thus, nobody has to learn the special (and lame) quirks of containers like LLMap<> and LLLinkedList<>.
Prefer the use of the containers available in the C++ standard template library. They are well tested and in common usage across the industry &mdash; thus, nobody has to learn the special (and lame) quirks of containers like <code>LLMap<></code> and <code>LLLinkedList<></code>.


This is one of the few places where specialized implementations can yield significant memory or speed improvements. Do not create those containers until you have profiling results that prove the container is the bottleneck.
This is one of the few places where specialized implementations can yield significant memory or speed improvements. Do not create those containers until you have profiling results that prove the container is the bottleneck.


Never use std::vector<bool>.
Never use <code>std::vector<bool></code>.


==== Memory Management ====
==== Memory Management ====
Use new and delete (and delete[] ) for your memory management. Don’t use malloc() and free().
Object allocation and deallocation should always be done using new and delete (or delete[] where appropriate).
 
Wherever possible, going forward, object lifetime should be managed using smart pointers, favoring the shared_pt, weak_ptr and unique_ptr types. When defining a pointer type declare a typedef to make it easily referenced elsewhere in the code, this typedef should be of the form:


Never store a pointer as object member data unless it is that object's job to delete the pointer. This means pointers should usually only be held in smart-pointer objects, as private member data allocated and deleted in the same cpp file, or in management specialization classes like LLRoundRobin. Never save a pointer extracted from a smart-pointer past the boundary of a stack frame. Always use handles provided by specialized containers when keeping a reference past a stack frame.
<syntaxhighlight lang="cpp">
typedef boost::shared_ptr<Class> ClassPtr;
typedef boost::intrusive_ptr<Class> ClassPtr;
</syntaxhighlight>
and
<syntaxhighlight lang="cpp">
typedef boost::weak_ptr<Class> ClassWPtr;
</syntaxhighlight>


Remember that C++ defines delete such that delete NULL doesn’t crash or cause errors. Thus, you don’t need this pattern:
Alternately, a class may define its own preferred pointer type as part of its definition.
<pre>  
 
if (foo) // unnecessary if(), delete works just fine on NULL pointers
<syntaxhighlight lang="cpp">
class MyClass
{
{
delete foo;
    typedef boost::shared_ptr<MyClass> ptr_t;
foo = NULL;
    typedef boost::weak_ptr<MyClass> wptr_t;
}
};
</pre>
 
/.../


==== Memory Usage Tracking ====
MyClass::ptr_t aPointerVariableToMyClass;
When using any memory allocation function - E.g. new, STL push_back in vectors, insert in an STL map, you need to preset a variable for memory tracking purposes. We have a build mode that allows us to track all memory allocation, but it does require that a memory allocation is preset to a type before the memory allocation takes place. E.g.
<pre>
if (!foo)
{
      LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
      foo = new LLFooType;
}
</pre>


The crucial part is the LLMemType mtw(LLMemType::MTYPE_APPROPRIATE_TYPE) - this presets the memory tracking system so it knows what kind of allocation is about to happen, so it can track it correctly.
</syntaxhighlight>


All the current types you can assign are defined with LLCommon/llmemtype.h. If you need a new one, just insert it and be sure you update the text of what that type is inside of llMemoryView.cpp.
New uses of the internal LLPointer<T> class should be avoided.


NOTE - you do NOT need to put this tracking line in where memory is Deallocated - you don't need it around delete, STL erase etc. The internal tracking system keeps track of what the memory is so when it's deleted you don't have to tell it what the memory was used for.
For legacy deallocation , not yet handled by managed pointers, remember that C++ defines delete NULL as being safe, therefore it is unnecessary to test for NULL before deleting.
<syntaxhighlight lang="cpp">
delete var;
var = null;
</syntaxhighlight>


==== Exception Handling ====
==== Exception Handling ====
Line 258: Line 326:


===== Resources =====
===== Resources =====
* [http://www.parashift.com/c++-faq-lite/exceptions.html c++ faq]
* [http://www.parashift.com/c++-faq-lite/exceptions.html C++ FAQ]
* [http://www.amazon.com/Exceptional-C%2B%2B-Engineering-Programming-Depth/dp/0201615622/ref=sr_1_2/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-2 exceptional c++]
* [http://www.amazon.com/Exceptional-C%2B%2B-Engineering-Programming-Depth/dp/0201615622/ref=sr_1_2/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-2 exceptional C++]
* [http://www.amazon.com/More-Exceptional-C%2B%2B-Engineering-Depth/dp/020170434X/ref=sr_1_1/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-1 more exceptional c++]
* [http://www.amazon.com/More-Exceptional-C%2B%2B-Engineering-Depth/dp/020170434X/ref=sr_1_1/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-1 more exceptional C++]


==== Error Messsages ====
==== Error Messages ====
* See [[Logging_System_Overview]] for detailed info on using the runtime logging system. (Test plan for logging system found at: [[Configurable Error Logging Test]])
* See [[Logging_System_Overview]] for detailed info on using the runtime logging system. (Test plan for logging system found at: [[Configurable Error Logging Test]])
* Do not wrap your messages in <code>#ifdef _DEBUG</code>, unless it is inside a performance critical loop.  The logging constructs compile to very small amounts of code, and are very efficiently skipped at run time.  And the ability to turn on your debug messages at run-time, even in a deployed release build, will make you very happy one day.
* Do not wrap your messages in <code>#ifdef _DEBUG</code>, unless it is inside a performance critical loop.  The logging constructs compile to very small amounts of code, and are very efficiently skipped at run time.  And the ability to turn on your debug messages at run-time, even in a deployed release build, will make you very happy one day.
Line 268: Line 336:
==== Obvious Things We All Know ====
==== Obvious Things We All Know ====
* No magic numbers
* No magic numbers
* Use TRUE and FALSE when working with BOOL (but prefer "bool" in general)
* Use <code>TRUE</code> and <code>FALSE</code> when working with <code>BOOL</code> (but prefer <code>bool</code> in general)
* Use NULL for pointer compares to 0
* Use <code>NULL</code> for pointer compares to <code>0</code>
* Use '\0' for character compares to 0
* Use <code>'\0'</code> for character compares to <code>0</code>
* Use const and inline rather than #define whenever possible. Prefer optimizing '''after''' you have proven that the code you want to inline actually has a significant benefit from in-lineing.
* Use <code>const</code> and <code>inline</code> rather than <code>#define</code> whenever possible. Prefer optimizing '''after''' you have proven that the code you want to inline actually has a significant benefit from in-lineing.
* Remember that C++ does not have garbage collection
* Remember that C++ does not have garbage collection
* Comment non-intuitive member variables
* Comment non-intuitive member variables
* Fix all compiler warnings.
* Fix all compiler warnings.
* Take advantage of the fact that gcc generates different, sometimes better, warnings than Visual Studio.
* Take advantage of the fact that gcc generates different, sometimes better, warnings than Visual Studio.
* Don't use assignments in while loops, eg while (foo = nextData()) as this causes gcc to emit warnings. Use while ((foo = nextData()) != NULL) or restructure the code.
* Don't use assignments in <code>while</code> loops, e.g. <code>while (foo = nextData())</code> as this causes gcc to emit warnings. Use <code>while ((foo = nextData()) != NULL)</code> or restructure the code.


=== Style ===
=== Style ===
==== Naming Convention ====
==== Naming Convention ====
The following name conventions apply to non-iterators:
The following name conventions apply to non-iterators:
* Names should include one or more English words that are relevant to the entity being named. Try to use non-programming words, like "EmployeeList" rather than "LinkedListOfStrings"
* Names should include one or more English words that are relevant to the entity being named. Try to use non-programming words, like <code>EmployeeList</code> rather than <code>LinkedListOfStrings</code>
* C functions are in lowercase, eg main(), update_world_time()
* C functions are in lowercase, e.g. <code>main()</code>, <code>update_world_time()</code>
* Local variables and parameters are in lowercase, eg frame_count
* Local variables and parameters are in lowercase, e.g. <code>frame_count</code>
** Local variables are '''not''' camel-case.  '''thisIsNotALocalVariable'''
** Local variables are '''not''' camel-case.  <code>thisIsNotALocalVariable</code>
* Class methods start with a lowercase character but are otherwise CamelCase, eg destroyWorld()
* Class methods start with a lowercase character but are otherwise CamelCase, e.g. <code>destroyWorld()</code>
* Non-static class member variables start with an 'm' followed by an uppercase character, eg mCameraPosition
* Non-static class member variables start with an '<code>m</code>' followed by an uppercase character, e.g. <code>mCameraPosition</code>
* Static class member variables start with an 's' followed by an uppercase character, eg sInstance
* Static class member variables start with an '<code>s</code>' followed by an uppercase character, e.g. <code>sInstance</code>
* Structures and Classes start with the uppercase 'LL', followed by the name with an uppercase first letter, eg LLMyClass
* Structures and Classes start with the uppercase '<code>LL</code>', followed by the name with an uppercase first letter, e.g. <code>LLMyClass</code>
* Enums start with the uppercase 'E', followed by the name with an uppercase first letter, eg EJerkyType. The enum values themselves should use the initials of the type, e.g. JT_TURKEY, JT_BEEF, etc.
* Enums start with the uppercase '<code>E</code>', followed by the name with an uppercase first letter, e.g. <code>EJerkyType</code>. The enum values themselves should use the initials of the type, e.g. <code>JT_TURKEY</code>, <code>JT_BEEF</code>, etc.
* Variables are in MKS (meters, kilograms, seconds) unless otherwise specified, e.g. frame_time is seconds, frame_time_ms is milliseconds.
* Variables are in MKS (meters, kilograms, seconds) unless otherwise specified, e.g. <code>frame_time</code> is seconds, <code>frame_time_ms</code> is milliseconds.
* Data sizes are bytes unless otherwise specified, e.g. size vs. size_bits.
* Data sizes are bytes unless otherwise specified, e.g. <code>size</code> vs. <code>size_bits</code>.
* Global variables start with a lowercase 'g', followed by the name with an uppercase first letter, eg gGlobalTimeCounter
* Global variables start with a lowercase '<code>g</code>', followed by the name with an uppercase first letter, e.g. <code>gGlobalTimeCounter</code>
* Compiler preprocessor directives should start with LL_ and consist of all uppercase letters separated with underscores, eg LL_DEGREES_TO_RADIANS()
** <strong>Note:</strong> In most future cases, prefer <code>LLSingleton</code> or <code>LLSimpleton</code> for class singletons. <code>LLSimpleton</code> in particular is more lightweight.
* Externally available const global variables that replace #defines should start with LL_ and consist of all uppercase letters separated with underscores, eg LL_SECONDS_IN_DAY
* Compiler preprocessor directives should start with LL_ and consist of all uppercase letters separated with underscores, e.g. <code>LL_DEGREES_TO_RADIANS()</code>
* File scoped const global variables that replace #defines need only consist of all uppercase letters separated with underscores, eg SECONDS_BETWEEN_CLEANUP
* Externally available <code>const</code> global variables that replace <code>#define</code>s should start with <code>LL_</code> and consist of all uppercase letters separated with underscores, e.g. <code>LL_SECONDS_IN_DAY</code>
* You may optionally append a suffix character signifying the type, for example, fontp is a font pointer, mTimeInSecondsf is a float
* File scoped <code>const</code> global variables that replace <code>#define</code>s need only consist of all uppercase letters separated with underscores, e.g. <code>SECONDS_BETWEEN_CLEANUP</code>
* You may optionally append a suffix character signifying the type, for example, <code>fontp</code> is a font pointer, <code>mTimeInSecondsf</code> is a float


==== Enums ====
==== Enums ====
Line 309: Line 378:
} ENewEnum;
} ENewEnum;
</pre>
</pre>
so that the typedef name (ENewEnum in the example) can be used as a type name rather than (enum e_new_enum).


==== Class Structure ====
==== Class Structure ====
Line 330: Line 400:
};
};
</pre>  
</pre>  
* Optionally, one line functions may be written in the class declaration header. Functions inlined for speed should go in the header. Other functions should go in the .cpp file.  
* Optionally, one line functions may be written in the class declaration header. Functions inlined for speed should go in the header. Other functions should go in the <code>.cpp</code> file.  
** NOTE: Functions inlined this way do not require the 'inline' compiler hint
*:NOTE: Functions inlined this way do not require the <code>inline</code> compiler hint
** NOTE: virtual functions can not actually be inlined by the compiler so they should reside in the .cpp file because  
*:NOTE: virtual functions can not be inlined by the compiler so they should reside in the <code>.cpp</code> file because
*** a) otherwise the linker has to resolve each instance into a single function
*:*otherwise the linker has to resolve each instance into a single function
*** b) it is misleading to imply that the function will be inlined when it will not be (e.g. don't use virtual functions inside performance critical inner loops)
*:*it is misleading to imply that the function will be inlined when it will not be (e.g. don't use virtual functions inside performance critical inner loops)
* Make sure that you initialize ALL member variables prior to use (often easiest to use an init() member function).
* Make sure that you initialize ALL member variables prior to use (often easiest to use an <code>init()</code> member function).


==== Interface Naming ====
==== Interface Naming ====
C++ Interfaces follow the above conventions for normal classes. They must consist entirely pure virtual methods and have a pure virtual destructor.  
C++ Interfaces follow the above conventions for normal classes. They must consist entirely pure virtual methods and have a pure virtual destructor.  
Interfaces must be named with "Interface" postfix.
Interfaces must be named with <code>Interface</code> postfix.
<pre>
<pre>
class LLExampleInterface
class LLExampleInterface
Line 353: Line 423:
Class declarations represent contracts and all non-private members are promises. It's important to promise as little as possible because it's much easier to expose more functionality later than it is to take it back. It also requires less precious time from each programmer attempting to use a given class when the non-private members are kept to a minimum. If you see something that's private, you know that you can safely ignore it, otherwise the designer is signaling that it is something that you might potentially want to use. Therefore please use these guidelines to keep visibility to a minimum, and remember that everything that applies to data members also applies to typedef, enum, class and method members.
Class declarations represent contracts and all non-private members are promises. It's important to promise as little as possible because it's much easier to expose more functionality later than it is to take it back. It also requires less precious time from each programmer attempting to use a given class when the non-private members are kept to a minimum. If you see something that's private, you know that you can safely ignore it, otherwise the designer is signaling that it is something that you might potentially want to use. Therefore please use these guidelines to keep visibility to a minimum, and remember that everything that applies to data members also applies to typedef, enum, class and method members.


* Prefer protected members to public, and prefer private members to both.
* Prefer <code>protected</code> members to <code>public</code>, and prefer <code>private</code> members to both.
* Prefer private inner classes to friends.
* Prefer <code>private</code> inner classes to <code>friend</code>s.
* Document all non-private members but only document implementation details in the .cpp file.
* Document all non-private members but only document implementation details in the <code>.cpp</code> file.
* Avoid creating a member in the first place when not absolutely needed. E.G. implement getNumPixels() { "return mWidth * mHeight; } rather than getNumPixels() { return mArea; } and having to always make sure that mArea is kept in sync with the current width and height. Remember: All class members are global to the class and all standard wisdom regarding globals apply.
* Avoid creating a member in the first place when not absolutely needed. E.g. implement <code>getNumPixels() { "return mWidth * mHeight; }</code> rather than <code>getNumPixels() { return mArea; }</code> and having to always make sure that <code>mArea</code> is kept in sync with the current width and height. Remember: All class members are global to the class and all standard wisdom regarding globals apply.
* Prefer accessor methods to accessible data.
* Prefer accessor methods to accessible data.
* Prefer local variables to member variables. Sometimes people create member variables as a way to share data between methods. When possible, it's better to create a local variable in one method and pass it as an argument to the ones that needed it.
* Prefer local variables to member variables. Sometimes people create member variables as a way to share data between methods. When possible, it's better to create a local variable in one method and pass it as an argument to the ones that needed it.
* Declare local variables as close to their point of use as possible. I.E. within nested blocks and as far down within its block as possible.
* Declare local variables as close to their point of use as possible. I.e. within nested blocks and as far down within its block as possible.


==== Static Members ====
==== Static Members ====
Use static class members for global variables and singletons. Since these are globals, avoid using statics when practical.
Use <code>static</code> class members for global variables and singletons. Since these are globals, avoid using statics when practical.
Avoid static class instances which will be globally constructed, use pointers and construct them in an initializer instead.
Avoid static class instances which will be globally constructed, use pointers and construct them in an initializer instead.
<pre>
<pre>
Line 382: Line 452:


==== Indentation ====
==== Indentation ====
Use 4 character tabs for indentation and ensure that your editor is not converting tabs to spaces. When possible, use indentation to clarify variable lists and Boolean operations.
 
Indentation must use spaces unless the file format requires tabs (Such as in Makefiles), with a default width of 4. When possible, use indentation to clarify variable lists and Boolean operations.
 
{{KBnote|Some codebases use a mix of tabs and spaces, new and edited lines of code should use spaces.
'''Do not bulk convert spacing unless the conversion has been blessed by maintainers.'''}}


==== Braces ====
==== Braces ====
Source code should be written using braces on a line by themselves, similar to the gnu standard (not the more compact k&r standard), i.e.
Source code should be written using braces on a line by themselves with the same indentation level as the control statement ([https://en.wikipedia.org/wiki/Indentation_style#Allman_style Allman style].) This is similar to, but not exactly, the [https://en.wikipedia.org/wiki/Indentation_style#GNU_style GNU style] (which indents the braces 2 spaces.)  The more compact [https://en.wikipedia.org/wiki/Indentation_style#K&R_style K&R style] (which puts the opening brace on the same line as the control statement) is right out, i.e.
<tt>
while (foo < 10)
<span  style="font-weight: bold; color: green">{</span>
}
</tt>


<pre>
while (foo < 10)
{
}
</pre>
Instead of:
Instead of:
<pre>
<tt>
while (foo < 10) {
while (foo < 10) <span style="font-weight: bold; color: red">{</span>
}
}
</pre>
</tt>
 
And:
And:
<pre>
<tt>
do  
do  
{
<span style="font-weight: bold; color: green">{</span>
do_stuff();
    do_stuff();
} while (foo < 10);
} while (foo < 10);
</pre>
</tt>
 
Instead of:
Instead of:
<pre>
<tt>
do {
do <span style="font-weight: bold; color: red">{</span>
do_stuff();
    do_stuff();
}
}
while (foo < 10);
while (foo < 10);
</pre>
</tt>
And:
And:
<pre>  
<tt>
if (foo < 10)
if (foo < 10)
{
<span style="font-weight: bold; color: green">{</span>
do_stuff();
    do_stuff();
}
}
else
else
{
<span style="font-weight: bold; color: green">{</span>
do_stuff();
    do_stuff();
}
}
</pre>
</tt>
 
Instead of:
Instead of:
<pre>
<tt>
if (foo < 10) {
if (foo < 10) <span style="font-weight: bold; color: red">{</span>
do_stuff();
    do_stuff();
} else {
}<span style="font-weight: bold; color: red"> else {</span>
do_stuff();
    do_stuff();
}
}
</pre>
</tt>


Use braces for clarity, even when not strictly required, e.g.
Use braces for clarity, even when not strictly required, e.g.
   
  <tt>
<pre>
for (j = 0; j < NUM_TEXTURES; ++j)
for (j = 0; j < NUM_TEXTURES; ++j)
<span style="font-weight: bold; color: green">{</span>
{
    do_stuff();
do_stuff();
<span style="font-weight: bold; color: green">}</span>
}
</tt>
</pre>
Instead of:
<tt>
for (j = 0; j < NUM_TEXTURES; ++j)
    <span style="font-weight: bold; color: red">do_stuff();</span>
</tt>
 
=====Single-line Functions=====
Function definitions also follow the brace indenting style described here, with one exception. For ''short'' functions which fit on a single line, doing so is allowed (and generally preferred).
<tt>
int increment(int x) { return x+1; }
</tt>


==== Comments ====
==== Comments ====
Use C++ style comments for single line comments and member variable descriptions.
Use C++ style comments for single line comments and member variable descriptions.
Use classic C style comments /* */ for temporarily commenting out large sections of code.
Use classic C style comments <code>/* */</code> for temporarily commenting out large sections of code.
 
 
We use the Doxygen system for generating documentation from header and source files. Doxygen supports a wide range of styles; this section provides recommendations for how it should be used in the Second Life Viewer project.
 
 
====Doxygen Comment Style====
 
Doxygen comments are distinguished from normal comments by an extra comment character at the beginning, and are associated with the adjacent code by their placement. Both C style comments and C++ single-line style comments are supported.
 
=====C++ single-line style comments=====
 
Doxygen interprets any C++ comment that has a '!' character at the beginning of a '//' comment, or an extra '*' character at the beginning of a '/* ... */' comment:
 
  //! A Doxygen single line comment
 
  /** A Doxygen single line comment */
 
=====C multiple-line style comments=====
 
  /// A Doxygen comment
  /// that extends over more than one line
  /// this form should be used whenever there is more than one line
 
'''note that triple slash does not work as a single line comment - it must have at least two lines'''
 
=====Comment Placement=====
 
Doxygen comments are, by default, associated with the code that immediately follows the comment:
 
  //! indicates whether or not the object currently allows any Foo actions.
  void isFooAllowed();
 
For some constructs it is more readable to place the comment after the code element it documents, which is accomplished by adding a '<' character after the Doxygen comment indicator. This may be used with either single or multi-line comments:
 
  void doFooAt( int offset ///< the offset into the Foo
              ,char* name ///< the name to look up for this Foo action
                          ///  in the FooMgr database.
              );
 
Placing the Doxygen comment after the element it documents in this way is preferred whenever the element is a member of a list, as in parameter declarations or enum values.
 
=====Class Documentation=====
 
A class declaration must include a detailed description comment preceding the class:
 
  /// FooFactory is a factory class that constructs instances of the
  /// subclasses of Foo based on information obtained from the
  /// foo-config file.
  class FooFactory
  {
 
 
The class comment is a good place to put general guidelines about how the methods in the class relate to one another.
 
=====Member Grouping=====
 
By default, Doxygen groups the members within a class based on heuristics that use the public/protected/private scope and the method signatures. For simple classes this usually works well, and may be used. When a class has many methods, it is usually better to explicitly control how they are grouped in the documentation, and to provide additional documentation at the group level. To explicitly control grouping, add the 'nosubgrouping' Doxygen command to the class comment:
 
  /// FooFactory is a factory class that constructs instances of the
  /// subclasses of Foo based on information obtained from the
  /// foo-config file.
  ///
  /// @nosubgrouping
  ///
  class FooFactory
  {
 
Each group is then formed of the following elements:
 
* An introducing Doxygen comment that supplies the group name using the 'name' Doxygen command,
* The detailed comment for the group,
* The Doxygen group start command '{',
* The declarations of the members in the group and accompanying documentation, and finally
* The Doxygen group end command '}'.
 
For example (preceeded here by a non-Doxygen comment with a line of '=' characters so that it reads better in the source file):
 
  // ================================================================================
  /// @name Searching
  ///
  /// The searching methods apply a compiled regular expression to a subject
  /// string.  All searching methods return a boolean result indicating whether
  /// or not some match was found in the subject.  To get information about
  /// the match, use the Results methods.
  ///
  ///@{
  ''... the methods in the group ...''
  ///@}
 
=====Member Function Documentation=====
 
Each member function should have:
 
* A brief single line description preceeding the member declaration
* Parameter descriptions following each parameter
* A more detailed description following the declaration, which should include a Doxygen 'returns' command if the method returns a value.
 
  /// Search a string for matches to this regular expression.
  bool Search( const char * subject ///< the string to be searched for a match
              ,int len = -1        ///< the length of the subject string
              ,int options = 0      ///< sum of any PCRE options flags
              );
  ///<  Apply the regular expression to the subject string.
  ///  Optional parameter len can be used to pass the subject's length to
  ///  Search(). If not specified (or less than 0), strlen() is used
  ///  internally to determine the length. Parameter options can contain
  ///  any combination of options; for options documentation, see 'man pcre'
  ///  @returns true if a match is found.
 
 
 
=====Usage Examples=====
 
Including examples in the documentation is strongly encouraged. To embed an example, use the Doxygen "@code ... @endcode" construct:
 
  ///<
  /// May only be called after a successful search
  /// and applies to the results of that call.
  /// @returns true if there was an ith match, false if not
  ///
  /// Example:@code
  /// RegEx matchBs("((B)B+)");
  /// UtlString getB;
  /// UtlString getBs;
  /// if (matchB.Search("xxaBBBBcyy"))
  /// {
  ///  matchB.MatchString(&getBs,0);
  ///  matchB.MatchString(&getB,2);
  /// }
  /// @endcode
  /// would set the UtlStrings
  ///  - getBs to "BBBB"
  ///  - getB  to "B"
 
=====Lists=====
 
Numbered and bulleted lists are supported in Doxygen comments using simple indentation conventions:
 
  ///< A numbered list is created using '#' characters:
  ///  # first numbered item
  ///  # second numbered item
  ///    # first item in nested numbered list (2.1)
  ///
  /// A bullet list uses '-' characters:
  ///  - first bullet
  ///  - second bullet
  ///    - nested bullet


==== Function Declaration ====
==== Function Declaration ====
The return value of the function should be on the same line as the function name, e.g.
The return type of the function should be on the same line as the function name, e.g.
 
<tt>
<span style="font-weight: bold; color: green">void</span> foo_func(S32 bar)
{
// do stuff
}
</tt>
 
Not on its own line:


<pre>
<tt>
void foo_func(S32 bar)
<span style="font-weight: bold; color: red">void</span>
{
foo_func(S32 bar)
// do stuff
{
}
// do stuff
</pre>
}
</tt>


== Python ==
== Python ==
Line 460: Line 704:


=== Source Files ===
=== Source Files ===
Unless it is an executable script, begin all Python source code files with this header:


<pre>
==== LGPL Licensed Code ====
Unless it is an executable script, begin all LGPL-licensed python source code files with this header:
 
<source lang="python">
"""\
"""\
@file filename
@file filename
Line 469: Line 715:
@brief brief description of the file
@brief brief description of the file


$LicenseInfo:firstyear=2009&license=viewergpl$
$LicenseInfo:firstyear=2011&license=viewerlgpl$
$/LicenseInfo$
Second Life Viewer Source Code
"""
Copyright (C) 2011, Linden Research, Inc.
</pre>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation;
version 2.1 of the License only.


The $LicenseInfo$ section is critically important. Choose viewergpl if you are not sure. Use the <i>linden/scripts/apply-license.py</i> script to turn the license declaration into a valid copyright and license section.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.


==== Python File Names ====
You should have received a copy of the GNU Lesser General Public
There is no need to prefix 'll' to every source file, but it may still be appropriate for the filename, for example, <code>llsd.py</code>.
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


Write Python scripts intended to be executed from the command line in a way that makes them importable as a module. Specifically, this means you should structure your code as follows:
Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
$/LicenseInfo$
"""
</source>


<pre>
The <code>$LicenseInfo$</code> section is critically important. Choose <code>viewerlgpl</code> if you are not sure.
#!/usr/bin/python


# Preamble, see below
==== MIT Licensed Code ====
...


# any class / utility functions here
Include a LICENSE, LICENSE.rst or LICENSE.md in your project with the following content and ensure it is included in any distribution archives of your software.
...


# the main "api" function
<source>
def do_stuff(arg1, arg2 ...):
MIT License
  ...


# the main() function which does the command line parsing and validation
Copyright (c) [year] [fullname]
# and invokes the "api" function.
def main(argv):
  ...
  return exit_code


# execute main() only if invoked directly:
Permission is hereby granted, free of charge, to any person obtaining a copy
if __name__ == "__main__":
of this software and associated documentation files (the "Software"), to deal
  sys.exit(main(sys.argv))
in the Software without restriction, including without limitation the rights
</pre>
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:


Every Python script should have a copy of the generic python wrapper shell script placed next to it, with the bare name - no .py extension. For example:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.


<pre>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#!/bin/sh
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</source>


MYPYTHON="${LLPYTHON:-python}"
==== Scripts ====


if test -r "$0".py
Write Python scripts intended to be executed from the command line in a way that makes them importable as a module and testable. One way to do this is by structuring your code as follows:
then
  exec "$MYPYTHON" "$0".py "$@"
elif test -r "$0".pyo
then
  exec "$MYPYTHON" "$0".pyo "$@"
elif test -r "$0".pyc
then
  exec "$MYPYTHON" "$0".pyc "$@"
else
  echo "Failed to locate '$0.py' or '$0.pyc'" >&2
  false
fi
</pre>


For production use, you then have the option of distributing only .pyo or .pyc files.
<source lang="python">
#!/usr/bin/env python3
# Doxygen header and license, see above


=== Style ===
import argparse
Library modules should never call functions or create objects which have side effects in the global process state prior to the first function call or object instantiation from an outside caller. This makes sure that different callers can configure the process state as necessary and not allow incorrect default values seep into the process state.


<pre>
# the main "api" function
# replace this:
def do_stuff(arg1, arg2 ...):
CONSTANT = 'value'
    ...


#with this:
# the main() function which does the command line parsing and validation
_g_constant = None
# and invokes the "api" function.
def get_constant():
def main(argv=None):
     global _g_constant
     parser = argparse.ArgumentParser(description="Little program to...")
     if _g_constant is None:
     parser.add_argument("arg1")
        _g_constant = 'value'
     parser.add_argument("arg1")
     return _g_constant
</pre>


=== Whitespace ===
    # When given a None argv, ArgumentParser defaults to using sys.argv[1:]
Python can't parse files that mix using tabs and spaces for indentation. We settled on 4 spaces as the standard indentation increment.
    # This is a convenient trick to making a testable main(), as it allows
    # test code to specify arguments programmatically and work with Exceptions
    # rather than SystemExit exceptions.


Put this in your ~/.emacs to get the desired behavior (note that this also turns on colored fonts, which you probably wanted anyway):
    args = parser.parse_args(argv)
<pre>
    do_stuff(args.arg1, arg2)
(defun ll-python-mode-hook()
  (font-lock-mode 1)
  (font-lock-fontify-buffer)
  (set-variable 'indent-tabs-mode nil)
  (set-variable 'py-indent-offset 4)
  (message "ll coding style function executed"))
(add-hook 'python-mode-hook 'll-python-mode-hook)
</pre>


(insert instructions on using python-mode if it's not with your default emacs install)


To convert tabs to spaces in Emacs:
# execute main() only if invoked directly:
* Make sure your tab width is 4
if __name__ == "__main__":
** In .emacs <code>(set-variable 'tab-width 4)</code> (optionally inside your ll-python-mode-hook())
    try:
** Immediately: M-x set-variable[ret] tab-width[ret] 4[ret]
        main()
* Set mark at start of file: M-< ^space
    except Exception as e:
* Jump to end of file: M->
        # Perform any user-friendly presentation of errors
* Convert tabs to spaces within marked range: M-x untabify
        sys.exit(str(e))
</source>


To use spaces instead of tabs in vi put this in ~/.vimrc:
" You probably already have these
set tabstop=4
set shiftwidth=4
" When reading a file or creating a new file, uses spaces not tabs
autocmd BufRead,BufNewFile *.py set expandtab
Another option for tabs/spaces:
" automatically expand tab keypresses into the appropriate number of spaces
set expandtab


=== PYTHONPATH ===
=== PYTHONPATH ===
To write scripts that can be run without an explicit PYTHONPATH, see linden/scripts/setup-path.py and the scripts that reference it.
To write scripts that can be run without an explicit <code>PYTHONPATH</code> create a python package using [https://github.com/pypa/setuptools setuptools] or [https://python-poetry.org/ poetry] and specify script entry points:


We maintain a python library in indra/lib/python.  In order to use the libraries in there, you have to set the PYTHONPATH environment variable to point to that directory, e.g.
setup.cfg (setuptools):
PYTHONPATH=/var/tmp/rdw/release/indra/lib/python python test.py
<source>
 
[options.entry_points]
To avoid having to specify the PYTHONPATH, you can instead use this snippet of code:
console_scripts =
<pre>
    executable-name = my_package.module:function
import sys
</source>
import os.path
# Look for indra/lib/python in all possible parent directories ...
# This is an improvement over the setup-path.py method used previously:
#  * the script may be relocated anywhere inside the source/runtime tree
#  * it doesn't depend on the current directory
#  * it doesn't depend on another file being present.


def add_indra_lib_path():
Activate your virtualenv and install the package using the editable flag:
    # Use a function to ensure that global scope isn't polluted
    root = os.path.realpath(__file__)
    # always insert the directory of the script in the search path
    libdir = os.path.dirname(root)
    if libdir not in sys.path:
        sys.path.insert(0, libdir)


    # Now go look for indra/lib/python in the parent dirs
<source>
    while root != os.path.sep:
pip install -e .
        root = os.path.dirname(root)
</source>
        libdir = os.path.join(root, 'indra', 'lib', 'python')
        if os.path.isdir(libdir):
            if libdir not in sys.path:
                sys.path.insert(0, libdir)
            return root
    print >> sys.stderr, "This script is not inside a valid installation."
    sys.exit(1)


# Use the return value to locate other files relative to the
...your scripts should be accessible on your PATH. Another way of working conveniently with scripts and virtualenvs is by using poetry:
# install root...
_root = add_indra_lib_path()
</pre>


Symlinking /opt/linden to your checkout should be deprecated. That's a bad dependency to have, and it doesn't play well with multiple devs on a station.
pyproject.html (poetry):
<source>
[tool.poetry.scripts]
executable-name = "my_package.module:function"
</source>


On our machines, we now have a standard library in <tt>/var/lib/python-support.python2.4/llindrapath.py</tt> that does all this. As such, you should be able to accomplish the path by including this line:
See [https://python-poetry.org/ poetry's documentation] for more information.


<pre>
=== Imports ===
import llindrapath
</pre>


=== imports ===
* Follow PEP-8's [https://peps.python.org/pep-0008/#imports import guidelines]
* Include only one module per line
* Include only one module per line
* Order your imports alphabetically (this gets a little vague with "from x in y" imports TODO: invent a rule that makes sense)
* Order your imports alphabetically (this gets a little vague with <code>from x in y</code> imports TODO: invent a rule that makes sense)
* Try not to pollute the module's namespace by importing individual functions and classes from other modules.
* Try not to pollute the module's namespace by importing individual functions and classes from other modules.


bad:
Bad:


<pre>
<source lang="python">
from my_module import foo
from os.path import join, dirname, realpath
from os.path import join, dirname, realpath
import requests
import socket, urllib2
import socket, urllib2
</pre>
</source>


good:
Good:


<pre>
<source lang="python">
import os.path
import os.path
import socket
import socket
import urllib2
import urllib2
</pre>
 
import requests
 
from my_module import foo
</source>

Latest revision as of 07:26, 29 May 2024

This document defines and formalizes the style of code produced at Linden Lab. Adherence to these conventions is critical to ensuring readable and maintainable code.

Some tools are available for checking some parts of these rules in the [1] repository.

Common Guidelines

Line Endings

All text files must use unix (linefeed only) line endings when submitted.

Exceptions are allowed only if the file in question:

  • Is used or relevant only on Windows
  • Requires DOS style (carriage-return linefeed) line endings for correctness.

Files that have DOS line endings must either:

  • have a suffix that is one of:
    • .bat
    • .sln
    • .vcxproj
  • or be located somewhere under a directory named windows or vstool

See How to avoid DOS line endings in Windows tools

Trailing Whitespace

Trailing whitespace must be trimmed from the ends of lines. Most text editors have a way to do this automatically.

Comments

When commenting, address the "why" and not the "what" unless the what is difficult to see. If what the code does requires commenting, also include why it has to be done that way so maintainers can follow your reasoning. Use complete sentences and correct spelling.

Prefer the use of doxygen compatible comment markup. (See Using Doxygen.)

Sometimes you know something is broken or a bad idea but need to do it anyway — please follow the special comment token guidelines to not confuse the next engineer. Append your name or initials and the date of the comment when using these special comment tokens.

Special Comment Tokens
Token Meaning Deprecated tokens
*FIX The associated code is actually broken and has known failure cases. All instances of these should be fixed before shipping. Do not use this tag for hacks or suggested development. Do not simply tag the code with the token alone or simply provide instructions since then it is unclear if that is how it is broken. For example: "// *FIX: invalidate stored textures when number of faces change" — is really unclear if it is an instruction or a buggy side effect of the code segment. FIXME BROKEN BUG
*HACK The associated code relies on external information or processes outside the normal control flow for the function to work. HACK
*TODO The associated code should be optimized, clarified, or made more reliable. Provide instructions along with why it is probably a good idea to execute on the comment. TODO
*NOTE This denotes that there is something tricky or special about the associated code above and beyond answering the typical 'what' and 'why.' NOTE "NOTA BENE" NB

Examples:

// *FIX: This will fail if the packets arrive out of order.

// *HACK: Filter out some extra information from this 
// packet and put it into the status bar since it is more 
// up to date than the last balance update.

// *TODO: This call does not always need to be made
// and is computationally expensive. Write a test to see
// if it must be made during this iteration.

// *NOTE: The tile is actually 257 pixels wide, so
// we have to fudge the texture coordinates a bit for this
// to look right.

Move Away From LLSD

When practical and prudent you should not use LLSD and should endeavor to remove it from code bases. We have better things to do than pour time into maintaining and dealing with the consequences of a home-grown serialization format. LLSD was okay in 200X, not 201X+. The obvious text-based alternative is JSON.

Unicode

Use UTF-8 for all string serialization and communication between processes. Do not use UTF-16 except when interfacing with Win32.

File Names

File names should be significant and of reasonable length, with no spaces or uppercase letters. Separate words in the name MUST be separated with underscore (like_this.cpp), because languages like Python cannot include module names with hyphens (will-not-import.py).

Dead Code

Do not leave commented-out code in any commits that you make. Delete that code instead.

Block-commented out code, including code disabled through #if <something-false> is especially problematic because it is not distinguishable from live code in a grep. Our version control systems are good enough that any code that was checked in is retrievable even after deletion.

C++

Source Files

All C++ source code files should begin with the header shown below:

/** 
 * @file file base name
 * @author optional
 * @brief brief description of the file
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2011, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

The $LicenseInfo$ section is critically important so that we can process the source files based on license agreements. If you're unsure, use viewerlgpl. The licenses we support are:

  • internal - something probably not meant for distribution unless we have an agreement with the third party.
  • viewergpl - deprecated this was used for pre-Snowstorm viewer distribution
  • viewerlgpl - the viewer and linden libraries distributed with it
  • mit - a basic MIT license for some libraries such as mulib, eventlet, and indra.ipc.

Replace the 2011 with the year the file is created.

All C++ source files should end in a newline. If not, GCC will fail with an error, as it enforces the C++ standard which requires one.

Headers should be included in the following order:

// All files MUST include one of the following first: 
//  (includes linden_preprocessor.h, stdtypes.h, and some common headers)
#include "llviewerprecompiledheaders.h"	// (NEWVIEW)
// OR
#include "llsimprecompiledheaders.h" // NEWSIM
// OR
#include "linden_common.h" // the ll libraries

#include "llfoo.h"		// Associated header, should have no hidden dependencies

#include <map>			// stl headers
#include <cmath>		// std headers
#include "vorbis/ogg.h"		// External library headers

#include "llbar.h"		// Other linden headers

File Names

All C++ source files should be prefixed with 'll'.

File names must end with the appropriate suffix:

  • cpp for ANSI C++ code
  • h for included declarations
  • inl for large chunks of inlined C++ code. Avoid this.
  • asm for assembly files. Do not do this without a note from some deity forgiving your transgressions.

Include Files

Include files should be specified using relative paths using the '/' character to help ensure portability. In the code relative paths should not be used. Instead, paths should be indicated in the relevant CMake file.

Compiler directives used to prevent multiple header file inclusion should use the #pragma once preprocessor directive. Include dependent headers in the header file, but use forward declarations where possible.

For example:

#pragma once

#include "llbar.h"

class LLReferencedData; // forward declaration (no need to include "llrefrenceddata.h")

// template forward declaration (no need to include "llotherreferenceddata.h" nor "llpointer.h")
class LLOtherReferencedData;
template<class Type> class LLPointer;

class LLFoo : public LLBar
{
public:
    LLFoo();
    void setData(LLReferencedData& ref_data);
    void useOtherData(LLPointer<LLOtherReferencedData>& other_data);
private:
    LLReferencedData* mRefData;
};

Basic Facilities

Linden Variable Types

The following basic types are defined in "stdtypes.h" and should be used to ensure clarity and cross platform functionality:

  • Integral Types
    • The first letter specifies 'S' for signed integers and 'U' for unsigned integers
    • The type ends with the number of bits used to represent the integer
    • Examples: S8 (signed 8-bit integer), U32 (unsigned 32 bit integer)
    • The integer types are: U8, S8, U16, S16, U32, S32, U64, S64
    • The char type may used when required by defualt C functions that need a char
  • Floating Point Types
    • F32 denotes a 32-bit floating point value ("float")
    • F64 denotes a 64-bit floating point value ("double")
  • Other Types
    • Prefer the use of the standard C++ bool. This is especially for comparators like operator< (using bool instead of BOOL will prevent lots of warnings when using STL). BOOL denotes a 32-bit integer value that is used to denote TRUE or FALSE. It is specifically not bool to prevent mistakes caused by "sizeof(bool)" being implementation specific.

Project Defines

  • Use LL_WINDOWS, LL_LINUX, and LL_DARWIN for platform specific code.
  • Use LL_RELEASE and LL_DEBUG for release and debug specific code.
  • LL_RELEASE_FOR_DOWNLOAD and LL_RELEASE are both defined in versions destined to be in a resident's hands.
  • Prefer testing for the positive when using #if, and use #if/#if ! instead of #ifdef/#ifndef. For example:
#if LL_DEBUG && !LL_DARWIN

instead of:

#ifndef LL_RELEASE && !defined(LL_DARWIN)

However, this construct is still useful:

#ifndef SOMETHING
#define SOMETHING SOMETHING_NEW
#endif

Usage

String Handling

See C++ Strings for more guidelines on strings in the C++ codebase.

Use std::string as a string container and the STRINGIZE() macro for constructing strings from multiple sources.

If you must use printf style formatting, use snprintf() or llformat(). Never use user supplied data (including configuration files) to specify formatting instructions to those functions.

Avoid using c-string libraries. If you are writing something where speed is critical, prefer the use of std::vector<char> since it's usage is less error prone, you can explicitly allocate or occupy memory, it is easy to null terminate with a single call to container.push_back('\0'), and it is char* compatible with a call to &container[0].

The following c functions are forbidden:

  • sprintf() and vsprintf() — most of the printf family of functions are like individual invitations to hackers to overflow a buffer. Don't use them.
  • strcpy() and strncpy() — both of these functions are dangerous and difficult to use correctly. For example, did you know that strncpy() does not null terminate if no null is found in the source?
  • strcat() and strncat() — these methods are inefficient in comparison to using ostringstream() and are easy to misuse. The strncat() function is almost forgivable, but please just avoid these.
  • gets() — Never, ever use this function.

Our legacy being what it is, there are some unavoidable cases where we are forced to use c-string functions. Here are some tips:

  • sscanf(), fscanf(), etc... — For these methods, always supply a maximum width for strings. If you do not know how to do this off the top of your head, do not use these functions. Even if you think you know, read the man page again.
  • snprintf(), fprintf(), vsnprintf(), etc.. — As noted above, never let anything other than the program determine the formatting. Never trust configuration files, assets, message system data, etc.

Unicode

Useful functions for dealing with UTF-8 can be found in llstring.h. Remember that truncating a UTF-8 string must be done on glyph rather than byte boundaries or your string may no longer be valid UTF-8. Many methods in std::string and LLString are not UTF-8 safe since they work on sizeof(char_t) byte boundaries. Similarly, the functions in that header file which do not contain 'ut8f' in the prototype are probably not UTF-8 safe.

Do not use UTF-16 except with interfacing with Win32.

Avoid the use of wide strings except when iteration speed is essential, or you are interfacing with the host operating system.

Trigraphs

A trigraph is a set of three characters that represents one character in the C character set. The set of trigraph sequences was defined in the ANSI Standard to allow users to use the full range of C characters, even if their keyboards do not implement the full C character set.

Each trigraph sequence is introduced by two question marks. The third character in the sequence indicates which character is being represented. The standard trigraphs and the characters they produce are:

??(   [
??/   \
??)   ]
??'   ^
??<   {
??!   |
??>   }
??-   ~

Using trigraphs anywhere in Second Life code is not allowed.

Containers

Prefer the use of the containers available in the C++ standard template library. They are well tested and in common usage across the industry — thus, nobody has to learn the special (and lame) quirks of containers like LLMap<> and LLLinkedList<>.

This is one of the few places where specialized implementations can yield significant memory or speed improvements. Do not create those containers until you have profiling results that prove the container is the bottleneck.

Never use std::vector<bool>.

Memory Management

Object allocation and deallocation should always be done using new and delete (or delete[] where appropriate).

Wherever possible, going forward, object lifetime should be managed using smart pointers, favoring the shared_pt, weak_ptr and unique_ptr types. When defining a pointer type declare a typedef to make it easily referenced elsewhere in the code, this typedef should be of the form:

typedef boost::shared_ptr<Class> ClassPtr; 
typedef boost::intrusive_ptr<Class> ClassPtr;

and

typedef boost::weak_ptr<Class> ClassWPtr;

Alternately, a class may define its own preferred pointer type as part of its definition.

class MyClass 
{
    typedef boost::shared_ptr<MyClass> ptr_t;
    typedef boost::weak_ptr<MyClass> wptr_t;
};

/.../

MyClass::ptr_t aPointerVariableToMyClass;

New uses of the internal LLPointer<T> class should be avoided.

For legacy deallocation , not yet handled by managed pointers, remember that C++ defines delete NULL as being safe, therefore it is unnecessary to test for NULL before deleting.

delete var;
var = null;

Exception Handling

Exceptions have been recently enabled for all platforms and targets in the main indra code base. If you are familiar with their use, you can use them judiciously in the code. Be sure to make sure the places where you are throwing exceptions will not cause resource leaks, and be sure to comment what linden exceptions might be thrown with the function declaration.

  • do not use exception specifications
  • do not throw in a destructor (ever)
  • read up on exceptions before using them
Resources

Error Messages

  • See Logging_System_Overview for detailed info on using the runtime logging system. (Test plan for logging system found at: Configurable Error Logging Test)
  • Do not wrap your messages in #ifdef _DEBUG, unless it is inside a performance critical loop. The logging constructs compile to very small amounts of code, and are very efficiently skipped at run time. And the ability to turn on your debug messages at run-time, even in a deployed release build, will make you very happy one day.

Obvious Things We All Know

  • No magic numbers
  • Use TRUE and FALSE when working with BOOL (but prefer bool in general)
  • Use NULL for pointer compares to 0
  • Use '\0' for character compares to 0
  • Use const and inline rather than #define whenever possible. Prefer optimizing after you have proven that the code you want to inline actually has a significant benefit from in-lineing.
  • Remember that C++ does not have garbage collection
  • Comment non-intuitive member variables
  • Fix all compiler warnings.
  • Take advantage of the fact that gcc generates different, sometimes better, warnings than Visual Studio.
  • Don't use assignments in while loops, e.g. while (foo = nextData()) as this causes gcc to emit warnings. Use while ((foo = nextData()) != NULL) or restructure the code.

Style

Naming Convention

The following name conventions apply to non-iterators:

  • Names should include one or more English words that are relevant to the entity being named. Try to use non-programming words, like EmployeeList rather than LinkedListOfStrings
  • C functions are in lowercase, e.g. main(), update_world_time()
  • Local variables and parameters are in lowercase, e.g. frame_count
    • Local variables are not camel-case. thisIsNotALocalVariable
  • Class methods start with a lowercase character but are otherwise CamelCase, e.g. destroyWorld()
  • Non-static class member variables start with an 'm' followed by an uppercase character, e.g. mCameraPosition
  • Static class member variables start with an 's' followed by an uppercase character, e.g. sInstance
  • Structures and Classes start with the uppercase 'LL', followed by the name with an uppercase first letter, e.g. LLMyClass
  • Enums start with the uppercase 'E', followed by the name with an uppercase first letter, e.g. EJerkyType. The enum values themselves should use the initials of the type, e.g. JT_TURKEY, JT_BEEF, etc.
  • Variables are in MKS (meters, kilograms, seconds) unless otherwise specified, e.g. frame_time is seconds, frame_time_ms is milliseconds.
  • Data sizes are bytes unless otherwise specified, e.g. size vs. size_bits.
  • Global variables start with a lowercase 'g', followed by the name with an uppercase first letter, e.g. gGlobalTimeCounter
    • Note: In most future cases, prefer LLSingleton or LLSimpleton for class singletons. LLSimpleton in particular is more lightweight.
  • Compiler preprocessor directives should start with LL_ and consist of all uppercase letters separated with underscores, e.g. LL_DEGREES_TO_RADIANS()
  • Externally available const global variables that replace #defines should start with LL_ and consist of all uppercase letters separated with underscores, e.g. LL_SECONDS_IN_DAY
  • File scoped const global variables that replace #defines need only consist of all uppercase letters separated with underscores, e.g. SECONDS_BETWEEN_CLEANUP
  • You may optionally append a suffix character signifying the type, for example, fontp is a font pointer, mTimeInSecondsf is a float

Enums

Enums generally are declared using this form:

typedef enum e_new_enum
{
  NE_ALPHA,
  NE_BETA,
  NE_GAMMA
} ENewEnum;

so that the typedef name (ENewEnum in the example) can be used as a type name rather than (enum e_new_enum).

Class Structure

Member functions come first, followed by member variables at the end. Write it assuming a time-pressured programmer is going to read it from top to bottom looking for something they can use, so put the exported functions at the top, eg:

class LLMyClass	
{
public:
	LLMyClass();
	~LLMyClass();

	void		init(S32 area, F32 priority);

	void		setVariable(BOOL variable)	{ mVariable = variable; }
	virtual void	draw();

private:
	BOOL		mVariable;
        static U32      sCount;
	etc.
};
  • Optionally, one line functions may be written in the class declaration header. Functions inlined for speed should go in the header. Other functions should go in the .cpp file.
    NOTE: Functions inlined this way do not require the inline compiler hint
    NOTE: virtual functions can not be inlined by the compiler so they should reside in the .cpp file because
    • otherwise the linker has to resolve each instance into a single function
    • it is misleading to imply that the function will be inlined when it will not be (e.g. don't use virtual functions inside performance critical inner loops)
  • Make sure that you initialize ALL member variables prior to use (often easiest to use an init() member function).

Interface Naming

C++ Interfaces follow the above conventions for normal classes. They must consist entirely pure virtual methods and have a pure virtual destructor. Interfaces must be named with Interface postfix.

class LLExampleInterface
{
public:
    virtual ~LLExampleInterface() = 0;

    virtual void example() = 0;
};

Member Visibility

Class declarations represent contracts and all non-private members are promises. It's important to promise as little as possible because it's much easier to expose more functionality later than it is to take it back. It also requires less precious time from each programmer attempting to use a given class when the non-private members are kept to a minimum. If you see something that's private, you know that you can safely ignore it, otherwise the designer is signaling that it is something that you might potentially want to use. Therefore please use these guidelines to keep visibility to a minimum, and remember that everything that applies to data members also applies to typedef, enum, class and method members.

  • Prefer protected members to public, and prefer private members to both.
  • Prefer private inner classes to friends.
  • Document all non-private members but only document implementation details in the .cpp file.
  • Avoid creating a member in the first place when not absolutely needed. E.g. implement getNumPixels() { "return mWidth * mHeight; } rather than getNumPixels() { return mArea; } and having to always make sure that mArea is kept in sync with the current width and height. Remember: All class members are global to the class and all standard wisdom regarding globals apply.
  • Prefer accessor methods to accessible data.
  • Prefer local variables to member variables. Sometimes people create member variables as a way to share data between methods. When possible, it's better to create a local variable in one method and pass it as an argument to the ones that needed it.
  • Declare local variables as close to their point of use as possible. I.e. within nested blocks and as far down within its block as possible.

Static Members

Use static class members for global variables and singletons. Since these are globals, avoid using statics when practical. Avoid static class instances which will be globally constructed, use pointers and construct them in an initializer instead.

class LLFoo
{
    static LLFoo* sInstance; // Don't do this -> static LLFoo sInstance;
    static void initClass();
};
...
//static
LLFoo* LLFoo::sInstance = NULL;

//static
void LLFoo::initClass()
{
    sInstance = new LLFoo();
}

Indentation

Indentation must use spaces unless the file format requires tabs (Such as in Makefiles), with a default width of 4. When possible, use indentation to clarify variable lists and Boolean operations.

KBnote.png Note: Some codebases use a mix of tabs and spaces, new and edited lines of code should use spaces. Do not bulk convert spacing unless the conversion has been blessed by maintainers.

Braces

Source code should be written using braces on a line by themselves with the same indentation level as the control statement (Allman style.) This is similar to, but not exactly, the GNU style (which indents the braces 2 spaces.) The more compact K&R style (which puts the opening brace on the same line as the control statement) is right out, i.e.

while (foo < 10)
{
}

Instead of:

while (foo < 10) {
}

And:

do 
{
    do_stuff();
} while (foo < 10);

Instead of:

do {
    do_stuff();
}
while (foo < 10);

And:

if (foo < 10)
{
    do_stuff();
}
else
{
    do_stuff();
}

Instead of:

if (foo < 10) {
    do_stuff();
} else {
    do_stuff();
}

Use braces for clarity, even when not strictly required, e.g.


for (j = 0; j < NUM_TEXTURES; ++j)
{
    do_stuff();
}

Instead of:

for (j = 0; j < NUM_TEXTURES; ++j)
    do_stuff();

Single-line Functions

Function definitions also follow the brace indenting style described here, with one exception. For short functions which fit on a single line, doing so is allowed (and generally preferred).


int increment(int x) { return x+1; }

Comments

Use C++ style comments for single line comments and member variable descriptions. Use classic C style comments /* */ for temporarily commenting out large sections of code.


We use the Doxygen system for generating documentation from header and source files. Doxygen supports a wide range of styles; this section provides recommendations for how it should be used in the Second Life Viewer project.


Doxygen Comment Style

Doxygen comments are distinguished from normal comments by an extra comment character at the beginning, and are associated with the adjacent code by their placement. Both C style comments and C++ single-line style comments are supported.

C++ single-line style comments

Doxygen interprets any C++ comment that has a '!' character at the beginning of a '//' comment, or an extra '*' character at the beginning of a '/* ... */' comment:

 //! A Doxygen single line comment
 /** A Doxygen single line comment */
C multiple-line style comments
 /// A Doxygen comment
 /// that extends over more than one line
 /// this form should be used whenever there is more than one line

note that triple slash does not work as a single line comment - it must have at least two lines

Comment Placement

Doxygen comments are, by default, associated with the code that immediately follows the comment:

 //! indicates whether or not the object currently allows any Foo actions.
 void isFooAllowed();

For some constructs it is more readable to place the comment after the code element it documents, which is accomplished by adding a '<' character after the Doxygen comment indicator. This may be used with either single or multi-line comments:

 void doFooAt( int offset ///< the offset into the Foo
              ,char* name ///< the name to look up for this Foo action
                          ///  in the FooMgr database.
             );

Placing the Doxygen comment after the element it documents in this way is preferred whenever the element is a member of a list, as in parameter declarations or enum values.

Class Documentation

A class declaration must include a detailed description comment preceding the class:

 /// FooFactory is a factory class that constructs instances of the
 /// subclasses of Foo based on information obtained from the
 /// foo-config file.
 class FooFactory
 {


The class comment is a good place to put general guidelines about how the methods in the class relate to one another.

Member Grouping

By default, Doxygen groups the members within a class based on heuristics that use the public/protected/private scope and the method signatures. For simple classes this usually works well, and may be used. When a class has many methods, it is usually better to explicitly control how they are grouped in the documentation, and to provide additional documentation at the group level. To explicitly control grouping, add the 'nosubgrouping' Doxygen command to the class comment:

 /// FooFactory is a factory class that constructs instances of the
 /// subclasses of Foo based on information obtained from the
 /// foo-config file.
 ///
 /// @nosubgrouping
 ///
 class FooFactory
 {

Each group is then formed of the following elements:

  • An introducing Doxygen comment that supplies the group name using the 'name' Doxygen command,
  • The detailed comment for the group,
  • The Doxygen group start command '{',
  • The declarations of the members in the group and accompanying documentation, and finally
  • The Doxygen group end command '}'.

For example (preceeded here by a non-Doxygen comment with a line of '=' characters so that it reads better in the source file):

 // ================================================================================
 /// @name Searching
 ///
 /// The searching methods apply a compiled regular expression to a subject
 /// string.  All searching methods return a boolean result indicating whether
 /// or not some match was found in the subject.  To get information about
 /// the match, use the Results methods.
 ///
 ///@{
 ... the methods in the group ...
 ///@}
Member Function Documentation

Each member function should have:

  • A brief single line description preceeding the member declaration
  • Parameter descriptions following each parameter
  • A more detailed description following the declaration, which should include a Doxygen 'returns' command if the method returns a value.
 /// Search a string for matches to this regular expression.
 bool Search( const char * subject ///< the string to be searched for a match
             ,int len = -1         ///< the length of the subject string
             ,int options = 0      ///< sum of any PCRE options flags
             );
 ///<  Apply the regular expression to the subject string.
 ///   Optional parameter len can be used to pass the subject's length to
 ///   Search(). If not specified (or less than 0), strlen() is used
 ///   internally to determine the length. Parameter options can contain
 ///   any combination of options; for options documentation, see 'man pcre'
 ///   @returns true if a match is found.


Usage Examples

Including examples in the documentation is strongly encouraged. To embed an example, use the Doxygen "@code ... @endcode" construct:

 ///<
 /// May only be called after a successful search
 /// and applies to the results of that call.
 /// @returns true if there was an ith match, false if not
 ///
 /// Example:@code
 /// RegEx matchBs("((B)B+)");
 /// UtlString getB;
 /// UtlString getBs;
 /// if (matchB.Search("xxaBBBBcyy"))
 /// {
 ///   matchB.MatchString(&getBs,0);
 ///   matchB.MatchString(&getB,2);
 /// }
 /// @endcode
 /// would set the UtlStrings
 ///  - getBs to "BBBB"
 ///  - getB  to "B"
Lists

Numbered and bulleted lists are supported in Doxygen comments using simple indentation conventions:

 ///< A numbered list is created using '#' characters:
 ///  # first numbered item
 ///  # second numbered item
 ///    # first item in nested numbered list (2.1)
 ///
 /// A bullet list uses '-' characters:
 ///  - first bullet
 ///  - second bullet
 ///    - nested bullet

Function Declaration

The return type of the function should be on the same line as the function name, e.g.

void foo_func(S32 bar)
{
	// do stuff
}

Not on its own line:

void
foo_func(S32 bar)
{
	// do stuff
}

Python

Our Python coding standards are generally based on http://www.python.org/dev/peps/pep-0008/, which should be considered authoritative for anything not covered here.

pylint can be used for stylistic as well as error checking. E.g. pylint --reports=n mydcode.py

Source Files

LGPL Licensed Code

Unless it is an executable script, begin all LGPL-licensed python source code files with this header:

"""\
@file filename
@author Optional author field
@date Optional iso8601 creation date
@brief brief description of the file

$LicenseInfo:firstyear=2011&license=viewerlgpl$
Second Life Viewer Source Code
Copyright (C) 2011, Linden Research, Inc.
 
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation;
version 2.1 of the License only.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
$/LicenseInfo$
"""

The $LicenseInfo$ section is critically important. Choose viewerlgpl if you are not sure.

MIT Licensed Code

Include a LICENSE, LICENSE.rst or LICENSE.md in your project with the following content and ensure it is included in any distribution archives of your software.

MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Scripts

Write Python scripts intended to be executed from the command line in a way that makes them importable as a module and testable. One way to do this is by structuring your code as follows:

#!/usr/bin/env python3
# Doxygen header and license, see above

import argparse

# the main "api" function
def do_stuff(arg1, arg2 ...):
    ...

# the main() function which does the command line parsing and validation
# and invokes the "api" function.
def main(argv=None):
    parser = argparse.ArgumentParser(description="Little program to...")
    parser.add_argument("arg1")
    parser.add_argument("arg1")

    # When given a None argv, ArgumentParser defaults to using sys.argv[1:]
    # This is a convenient trick to making a testable main(), as it allows
    # test code to specify arguments programmatically and work with Exceptions
    # rather than SystemExit exceptions.

    args = parser.parse_args(argv)
    do_stuff(args.arg1, arg2)


# execute main() only if invoked directly:
if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        # Perform any user-friendly presentation of errors
        sys.exit(str(e))


PYTHONPATH

To write scripts that can be run without an explicit PYTHONPATH create a python package using setuptools or poetry and specify script entry points:

setup.cfg (setuptools):

[options.entry_points]
console_scripts =
    executable-name = my_package.module:function

Activate your virtualenv and install the package using the editable flag:

pip install -e .

...your scripts should be accessible on your PATH. Another way of working conveniently with scripts and virtualenvs is by using poetry:

pyproject.html (poetry):

[tool.poetry.scripts]
executable-name = "my_package.module:function"

See poetry's documentation for more information.

Imports

  • Follow PEP-8's import guidelines
  • Include only one module per line
  • Order your imports alphabetically (this gets a little vague with from x in y imports TODO: invent a rule that makes sense)
  • Try not to pollute the module's namespace by importing individual functions and classes from other modules.

Bad:

from my_module import foo
from os.path import join, dirname, realpath
import requests
import socket, urllib2

Good:

import os.path
import socket
import urllib2

import requests

from my_module import foo