Difference between revisions of "Coding Standards"

From Sirikata Wiki
Jump to navigation Jump to search
m
 
(6 intermediate revisions by 4 users not shown)
Line 1: Line 1:
.source-cpp { border: 1px solid black; margin: 1em }
+
In general, follow the [http://www.ogre3d.org/wiki/index.php/DevelopmentProcedures Ogre coding style]. If some code requires formatting not covered by this page, please add the appropriate guidelines for such code to this list.
In general, follow the Ogre coding style. If some code requires
 
formatting not covered by this page, please add the appropriate
 
guidelines for such code to this list.
 
  
* Adhere to the ISO C++ standard. If you must put in non-standard
+
* Adhere to the ISO C++ standard. If you must put in non-standard workarounds for specific compilers, document this and make sure the code compiles as expected on a standards-compliant compiler as well.
workarounds for specific compilers, document this and make sure the
+
* Indent with four spaces: '''no tabs'''. (Note that this is different from Ogre.)
code compiles as expected on a standards-compliant compiler as well.
 
* Indent with four spaces: '''no tabs'''. (Note that this is different
 
from Ogre.)
 
 
* Use Unix linebreaks (LF).
 
* Use Unix linebreaks (LF).
* End each file with a newline, and remove all trailing whitespace
+
* End each file with a newline, and remove all trailing whitespace from individual lines.
from individual lines.
+
* Endeavor to be <code>const</code> correct everywhere: modern compilers can use this to a huge advantage.
* Endeavor to be <code>const</code> correct everywhere: modern
+
* Opening braces are always on the same line as the keyword, except for class and function definitions.
compilers can use this to a huge advantage.
 
* Opening braces are always on the same line as the keyword, except
 
for class and function definitions.
 
 
<source lang="cpp">
 
<source lang="cpp">
 
namespace MyNamespace {
 
namespace MyNamespace {
Line 34: Line 25:
 
}  // namespace MyNamespace
 
}  // namespace MyNamespace
 
</source>
 
</source>
* Functions definitions that are sufficiently short to inline should
+
* Functions definitions that are sufficiently short to inline should be defined in the with the class declaration (usually in the .hpp) and live entirely on one line, as in myFunctionOne() below.
be defined in the with the class declaration (usually in the .hpp) and
+
* Function definitions that are longer than a few lines should not be implemented within the class definition, as in myErf() below. There are several reasons for this: (1) the class definition is used by programmers as an API reference, and implementation details make it difficult to find the information; (2) functions defined within the class declaration are considered to be inline functions, and if longer than a few lines will lead to code bloat and be difficult to trace in a debugger.
live entirely on one line:
 
 
<source lang="cpp">
 
<source lang="cpp">
 
class MyClass
 
class MyClass
 
{
 
{
     MyClass::myFunctionOne() { return 1; }
+
     int myFunctionOne() const { return 1; }
 +
    double myErf(double x) const;
 +
};
 +
 
 +
double MyClass::myErf(double x) const
 +
{
 +
    if (x < 0)
 +
        return -myErf(-x);
 +
    const double delta = 0.01;
 +
    double acc = 0;
 +
    for (double y = delta; y < x; y += delta)
 +
        acc += delta * exp(-y * y);
 +
    y -= delta;
 +
    acc += (x - y) * exp(-x * x);
 +
    acc *= 2. / M_PI;
 +
    return acc;
 
}
 
}
 
</source>
 
</source>
* All names are in camel notation, e.g. <code>MyClass</code>,
+
 
<code>MyNamespace</code>, <code>myFunction</code>,
+
* All names are in camel notation, e.g. <code>MyClass</code>, <code>MyNamespace</code>, <code>myFunction</code>, <code>mMemberVariable</code> and should be ''maximally descriptive''. That is, a function that computes the length of something should be called <code>myLength()</code> instead of <code>Len()</code>, and a loop that iterates over a list of Spimes should look like <code>for (int spime = 0 ; spime < spimeLength; spime++)</code> instead of <code>for (int i = 0; i < spimeLength; i++)</code>.
<code>mMemberVariable</code> and should be ''maximally descriptive''.
 
That is, a function that computes the length of something should be
 
called <code>myLength()</code> instead of <code>Len()</code>, and a
 
loop that iterates over a list of Spimes should look like <code>for
 
(int spime = 0 ; spime < spimeLength; spime++)</code> instead of
 
<code>for (int i = 0; i < spimeLength; i++)</code>.
 
 
* All function names start with a lower-case letter.
 
* All function names start with a lower-case letter.
 
* Member variables start with the lowercase letter 'm'.
 
* Member variables start with the lowercase letter 'm'.
 
* Stick to 155-character screen widths as far as possible.
 
* Stick to 155-character screen widths as far as possible.
* Write the '''minimum amount of code''' that does what you need to
+
* Write the '''minimum amount of code''' that does what you need to do.  This means that
do.  This means that
 
 
<source lang="cpp">
 
<source lang="cpp">
 
if (condition) value = myFunction() + 1. / 3 * 5;
 
if (condition) value = myFunction() + 1. / 3 * 5;
Line 65: Line 63:
 
}
 
}
 
</source>
 
</source>
In the vast majority of cases, if the compiler doesn't need
+
In the vast majority of cases, if the compiler doesn't need parentheses or brackets, you don't need them either.
parentheses or brackets, you don't need them either.
+
* Use whitespace where appropriate. In particular, clearly separate successive blocks, function declarations etc (a good model is the <code>MyClass</code> code to illustrate indentation below). Also, put spaces in mathematical expressions, e.g. <code>x = a + b + c;</code>, not <code>x=a+b+c;</code>, and after delimiters in general, especially commas (e.g. <code>int a, b;</code>, not <code>int a,b;</code>).
* Use whitespace where appropriate. In particular, clearly separate
+
* In general, you should put comments wherever you feel that they enhance the readability of the code.  However, if you're writing good
successive blocks, function declarations etc (a good model is the
+
code, there should be very few instances where this is the case. Redundant comments (like the one below) are pernicious and should be avoided:
<code>MyClass</code> code to illustrate indentation below). Also, put
 
spaces in mathematical expressions, e.g. <code>x = a + b + c;</code>,
 
not <code>x=a+b+c;</code>, and after delimiters in general, especially
 
commas (e.g. <code>int a, b;</code>, not <code>int a,b;</code>).
 
* In general, you should put comments wherever you feel that they
 
enhance the readability of the code.  However, if you're writing good
 
code, there should be very few instances where this is the case.
 
Redundant comments (like the one below) are pernicious and should be
 
avoided:
 
 
<source lang="cpp">
 
<source lang="cpp">
 
if (programOptions[OPTION_MODE] == "server") { // server
 
if (programOptions[OPTION_MODE] == "server") { // server
Line 103: Line 92:
 
     ++x;  // Faster than x = x + 1?
 
     ++x;  // Faster than x = x + 1?
 
</source>
 
</source>
* Function definitions should always be preceded by a comment of the
+
* Function definitions should always be preceded by a comment of the following form:
following form:
 
 
<source lang="cpp">
 
<source lang="cpp">
 
//#####################################################################
 
//#####################################################################
Line 140: Line 128:
 
};  // class MyClass
 
};  // class MyClass
 
</source>
 
</source>
* When defining a function (except when it is inlined within a class
+
* When defining a function (except when it is inlined within a class declaration), put its name and return type on a separate line, indented as little as possible. That is:
declaration), put its name and return type on a separate line,
 
indented as little as possible. That is:
 
 
<source lang="cpp">
 
<source lang="cpp">
 
[template <class T>] [inline]
 
[template <class T>] [inline]
Line 158: Line 144:
 
}
 
}
 
</source>
 
</source>
* Write typenames so they can be read right-to-left, e.g.
+
* Write typenames so they can be read right-to-left, e.g. <code>std::string const & name</code> can be read as: "<code>name</code> is a reference (<code>&</code>) to an immutable (<code>const</code>) string", and <code>int * const p</code> can be read as: "<code>p</code> is an immutable (<code>const</code>) pointer (<code>*</code>) to an integer". Note that in the latter case, the integer can be hanged through <code>p</code>, but <code>p</code> cannot be reseated to point to something else, which is made clear by the right-to-left reading.
<code>std::string const & name</code> can be read as:
+
* When a function has so many arguments that it cannot fit in 155 columns, write its declaration like this:
"<code>name</code> is a reference (<code>&</code>) to an immutable
 
(<code>const</code>) string", and <code>int * const p</code> can be
 
read as: "<code>p</code> is an immutable (<code>const</code>) pointer
 
(<code>*</code>) to an integer". Note that in the latter case, the
 
integer can be changed through <code>p</code>, but <code>p</code>
 
cannot be reseated to point to something else, which is made clear by
 
the right-to-left reading.
 
* When a function has so many arguments that it cannot fit in 155
 
columns, write its declaration like this:
 
 
<source lang="cpp">
 
<source lang="cpp">
 
void foo(int a, int b, int c, int d, int e,
 
void foo(int a, int b, int c, int d, int e,
Line 181: Line 158:
 
}
 
}
 
</source>
 
</source>
* Use "get" and "set" as prefixes to accessor and modifier functions.
+
* Use "get" and "set" as prefixes to accessor and modifier functions. i.e. use <code>int getAge() const</code> and <code>void setAge(int age)</code>, not <code>int age() const</code> and <code>void age(int a)</code>.
i.e. use <code>int getAge()</code> and <code>void setAge(int
+
* Prefer clear, well written code that reads naturally to documentation.  Document all functions for Doxygen and reference purposes, but within functions or for private functions, document only
age)</code>, not <code>int age()</code> and <code>void age(int
+
as necessary.  However, don't under-document code.  Prefer over-documentation if you are unsure if documentation is necessary.
a)</code>.
 
* Prefer clear, well written code that reads naturally to
 
documentation.  Document all functions for Doxygen and reference
 
purposes, but within functions or for private functions, document only
 
as necessary.  However, don't under-document code.  Prefer
 
over-documentation if you are unsure if documentation is necessary.
 
 
* Prefer references to smart pointers, and smart pointers to raw pointers.
 
* Prefer references to smart pointers, and smart pointers to raw pointers.
* All text (comments, identifiers etc) should be in English except for
+
* All text (comments, identifiers etc) should be in English except for technical terms for which it's conventional to use another language. For consistency, prefer the American spelling for identifiers.
technical terms for which it's conventional to use another language.
+
* Don't write <code>if (a) delete a;</code>.  <code>delete</code> may safely be called on <code>NULL</code>.
For consistency, prefer the American spelling for identifiers.
 
* Don't write <code>if (a) delete a;</code>.  <code>delete</code> may
 
safely be called on <code>NULL</code>.
 
 
= Logging =
 
= Logging =
* All debugging printouts should use our new threadsafe logging
+
* All debugging printouts should use our new threadsafe logging mechanism which is customizable on a per-user basis through config files and/or command line options.
mechanism which is customizable on a per-user basis through config
+
* The Logging.hpp contains macros that should be used to construct the output stream to write logs to.
files and/or command line options.
 
* The Logging.hpp contains macros that should be used to construct the
 
output stream to write logs to.
 
 
* Prefer your subsystem macro:
 
* Prefer your subsystem macro:
* If you are working on graphics and you wish to write a log that will
+
* Let's say you are working on a plugin called "mymodule". If you wish to write a log that will be seen by default, write:
be seen by default write
 
 
<source lang="cpp">
 
<source lang="cpp">
     MERU_LOG_OGRE << "Everyone will see this"
+
     SILOG(mymodule,info,"Everyone will see this")
 
</source>
 
</source>
* If you are working on graphics and wish to write a log that will
+
* If you are working on graphics and wish to write a log that will assist you in debugging and should only be seen by others working on
assist you in debugging and should only be seen by others working on
+
the graphics subsystem do:
the graphics subsystem do
 
 
<source lang="cpp">
 
<source lang="cpp">
     MERU_L_OGRE(debug) << "Only Ogre graphics developers will see
+
     SILOG(mymodule,debug,"Only Ogre graphics developers will see this in the meru system")
this in the meru system"
 
 
</source>
 
</source>
* If you are working on graphics and need to spam the console with
+
* If you are working on graphics and need to spam the console with information to get at a bug use:
information to get at a bug use
 
 
<source lang="cpp">
 
<source lang="cpp">
     MERU_L_OGRE(insane_debug) <<"This is only for the folks willing
+
     SILOG(mymodule,insane,"This is only for the folks willing to sacrifice serious performance to view logs")
to sacrifice serious performance to view logs"
 
 
</source>
 
</source>
* subsystems include MERU_L_OGRE, MERU_L_CDN, MERU_L_EVENT  feel free
+
* In general, there will be about one subsystem per directory.
to add others where necessary by following the same path of coding as
+
* Level can be one of [insane,debug,info,warning,error,fatal].
the other 3 subsystem logs
+
 
* If your log message is generic to overall Meru and does not belong
+
Here's an example:
in a category (eg: ready to serve users) use the MERU_LOG(<level>)
 
where level can be one of
 
[insane_debug,debug,info,warning,error,fatal] Here's an example
 
 
<source lang="cpp">
 
<source lang="cpp">
MERU_LOG(debug) << "Class Hierarchy Ready, go to loading phase";
+
SILOG(mymodule, debug, "Class Hierarchy Ready, go to loading phase");
MERU_LOG(info) << "Server ready to accept clients";
+
SILOG(mymodule, info, "Server ready to accept clients");
MERU_LOG(warning) << "Value of ProxyObject should not be null";
+
SILOG(mymodule, warning, "Value of ProxyObject should not be null");
MERU_LOG(error) << "Empty config file. Wrong path? "<< mypath;
+
SILOG(mymodule, error, "Empty config file. Wrong path? "<< mypath);
MERU_LOG(fatal) << "Cannot recover from "<<err<<" aborting";
+
SILOG(mymodule, fatal, "Cannot recover from "<<err<<": aborting");
 
</source>
 
</source>
 
* There are macros for each of these debug levels
 
* There are macros for each of these debug levels
* Turn on logging with --logging=debug or turn it down to serious
+
* FIXME: How do do this in sirikata? Turn on logging with --loglevel debug or turn it down to serious only errors with --loglevel error
only errors with --logging=error
+
* Turning on subsystem logs is easy: just run with the command line argument --moduleloglevel module1=debug,module2=warning for example.
* Turning on subsystem logs is easy: just run meru with the command
+
* For example, if you only want to display networking prints, you can add a command line option: "--moduleloglevel transfer=fatal,ogre=fatal,task=fatal,resource=fatal"
line argument --ogrelog=debug or --ogrelog=all of --ogrelog=info or
+
/*
any level
+
* You can tune what and where prints out in logs by selecting various options from
* You can turn off logging by doing ./configure --disable-logging to
 
avoid most performance effects
 
* You can tune what and where prints out in logs by selecting various
 
options from
 
 
** --logfile=log.txt Choose which file to write to
 
** --logfile=log.txt Choose which file to write to
 
** --log-write-stdout=0 Choose not to write to stdout terminal
 
** --log-write-stdout=0 Choose not to write to stdout terminal
Line 252: Line 205:
 
** --log-index=0 Choose not to write index # to log
 
** --log-index=0 Choose not to write index # to log
 
** --log-function=1 Choose to log which function printed the message
 
** --log-function=1 Choose to log which function printed the message
** --log-fileline=0 Choose not to log which file and line number the
+
** --log-fileline=0 Choose not to log which file and line number the message was printed on
message was printed on
+
** --log-leveltype=0 Choose not to log the level of the log (debug,warning,info,etc)
** --log-leveltype=0 Choose not to log the level of the log
+
*/
(debug,warning,info,etc)
 

Latest revision as of 03:17, 19 August 2009

In general, follow the Ogre coding style. If some code requires formatting not covered by this page, please add the appropriate guidelines for such code to this list.

  • Adhere to the ISO C++ standard. If you must put in non-standard workarounds for specific compilers, document this and make sure the code compiles as expected on a standards-compliant compiler as well.
  • Indent with four spaces: no tabs. (Note that this is different from Ogre.)
  • Use Unix linebreaks (LF).
  • End each file with a newline, and remove all trailing whitespace from individual lines.
  • Endeavor to be const correct everywhere: modern compilers can use this to a huge advantage.
  • Opening braces are always on the same line as the keyword, except for class and function definitions.
namespace MyNamespace {

class MyClass {};

MyClass::MyClass(int val1, int val2)
: mVar1(val1), mVar2(val2)
{
    if(a) {
        myFunctionOne();
        myFunctionTwo();
    }else {
        myFunctionThree();        
    }
}

}  // namespace MyNamespace
  • Functions definitions that are sufficiently short to inline should be defined in the with the class declaration (usually in the .hpp) and live entirely on one line, as in myFunctionOne() below.
  • Function definitions that are longer than a few lines should not be implemented within the class definition, as in myErf() below. There are several reasons for this: (1) the class definition is used by programmers as an API reference, and implementation details make it difficult to find the information; (2) functions defined within the class declaration are considered to be inline functions, and if longer than a few lines will lead to code bloat and be difficult to trace in a debugger.
class MyClass
{
    int myFunctionOne() const { return 1; }
    double myErf(double x) const;
};

double MyClass::myErf(double x) const
{
    if (x < 0)
        return -myErf(-x);
    const double delta = 0.01;
    double acc = 0;
    for (double y = delta; y < x; y += delta)
        acc += delta * exp(-y * y);
    y -= delta;
    acc += (x - y) * exp(-x * x);
    acc *= 2. / M_PI;
    return acc;
}
  • All names are in camel notation, e.g. MyClass, MyNamespace, myFunction, mMemberVariable and should be maximally descriptive. That is, a function that computes the length of something should be called myLength() instead of Len(), and a loop that iterates over a list of Spimes should look like for (int spime = 0 ; spime < spimeLength; spime++) instead of for (int i = 0; i < spimeLength; i++).
  • All function names start with a lower-case letter.
  • Member variables start with the lowercase letter 'm'.
  • Stick to 155-character screen widths as far as possible.
  • Write the minimum amount of code that does what you need to do. This means that
if (condition) value = myFunction() + 1. / 3 * 5;

is much preferable to

if (condition == true) {
    value = (this->myFunction() + (1.0 / 3.0) * 5.0));
}

In the vast majority of cases, if the compiler doesn't need parentheses or brackets, you don't need them either.

  • Use whitespace where appropriate. In particular, clearly separate successive blocks, function declarations etc (a good model is the MyClass code to illustrate indentation below). Also, put spaces in mathematical expressions, e.g. x = a + b + c;, not x=a+b+c;, and after delimiters in general, especially commas (e.g. int a, b;, not int a,b;).
  • In general, you should put comments wherever you feel that they enhance the readability of the code. However, if you're writing good

code, there should be very few instances where this is the case. Redundant comments (like the one below) are pernicious and should be avoided:

if (programOptions[OPTION_MODE] == "server") { // server
  • Doxygen comments should be written like this:
/** Short description of function foo.
 *  Longer description of function foo that may span many lines
 *  like this blah blah blah...  Return type should be on same line
 *  as start of function name to support eventDispatch lookups
 */
void foo()
{
    ...
}
  • Non-Doxygen comments should be written like this:
// Let x be the sum of a and b.
x = a + b;

// Now loop over values of i from 0 to 9, adding
// 1 to x at each stage.
for (int i = 0; i < 10; ++i)
    ++x;  // Faster than x = x + 1?
  • Function definitions should always be preceded by a comment of the following form:
//#####################################################################
// myFunction
//#####################################################################
void myFunction()
{
    ...
}
  • A namespace declaration does not induce a level of indentation.
  • Indent within a class as follows:
class MyClass
{

  public:

    /** Short description of foo.
     *  Long description of foo.
     */
    void foo();

    /** smallFoo has only a short description. */
    void smallFoo();


  private:

    /** Short description of bar.
     *  Long description of bar.
     */
    void bar();

};  // class MyClass
  • When defining a function (except when it is inlined within a class declaration), put its name and return type on a separate line, indented as little as possible. That is:
[template <class T>] [inline]
void MyClass::foo(int n)
{
    ...
}
  • Initialization lists are written like this:
MyClass::MyClass(std::string const & name, int age, int uuid)
: ParentClass(uuid), mName(name), mAge(age)
{
    ...
}
  • Write typenames so they can be read right-to-left, e.g. std::string const & name can be read as: "name is a reference (&) to an immutable (const) string", and int * const p can be read as: "p is an immutable (const) pointer (*) to an integer". Note that in the latter case, the integer can be hanged through p, but p cannot be reseated to point to something else, which is made clear by the right-to-left reading.
  • When a function has so many arguments that it cannot fit in 155 columns, write its declaration like this:
void foo(int a, int b, int c, int d, int e,
         int f, int g, int h, int i, int j);

... and its definition like this:

void foo(int a, int b, int c, int d, int e,
         int f, int g, int h, int i, int j)
{
    ...
}
  • Use "get" and "set" as prefixes to accessor and modifier functions. i.e. use int getAge() const and void setAge(int age), not int age() const and void age(int a).
  • Prefer clear, well written code that reads naturally to documentation. Document all functions for Doxygen and reference purposes, but within functions or for private functions, document only

as necessary. However, don't under-document code. Prefer over-documentation if you are unsure if documentation is necessary.

  • Prefer references to smart pointers, and smart pointers to raw pointers.
  • All text (comments, identifiers etc) should be in English except for technical terms for which it's conventional to use another language. For consistency, prefer the American spelling for identifiers.
  • Don't write if (a) delete a;. delete may safely be called on NULL.

Logging

  • All debugging printouts should use our new threadsafe logging mechanism which is customizable on a per-user basis through config files and/or command line options.
  • The Logging.hpp contains macros that should be used to construct the output stream to write logs to.
  • Prefer your subsystem macro:
  • Let's say you are working on a plugin called "mymodule". If you wish to write a log that will be seen by default, write:
     SILOG(mymodule,info,"Everyone will see this")
  • If you are working on graphics and wish to write a log that will assist you in debugging and should only be seen by others working on

the graphics subsystem do:

     SILOG(mymodule,debug,"Only Ogre graphics developers will see this in the meru system")
  • If you are working on graphics and need to spam the console with information to get at a bug use:
     SILOG(mymodule,insane,"This is only for the folks willing to sacrifice serious performance to view logs")
  • In general, there will be about one subsystem per directory.
  • Level can be one of [insane,debug,info,warning,error,fatal].

Here's an example:

SILOG(mymodule, debug, "Class Hierarchy Ready, go to loading phase");
SILOG(mymodule, info, "Server ready to accept clients");
SILOG(mymodule, warning, "Value of ProxyObject should not be null");
SILOG(mymodule, error, "Empty config file. Wrong path? "<< mypath);
SILOG(mymodule, fatal, "Cannot recover from "<<err<<": aborting");
  • There are macros for each of these debug levels
  • FIXME: How do do this in sirikata? Turn on logging with --loglevel debug or turn it down to serious only errors with --loglevel error
  • Turning on subsystem logs is easy: just run with the command line argument --moduleloglevel module1=debug,module2=warning for example.
  • For example, if you only want to display networking prints, you can add a command line option: "--moduleloglevel transfer=fatal,ogre=fatal,task=fatal,resource=fatal"

/*

  • You can tune what and where prints out in logs by selecting various options from
    • --logfile=log.txt Choose which file to write to
    • --log-write-stdout=0 Choose not to write to stdout terminal
    • --log-write-stderr=1 Choose to write to stderr terminal
    • --log-time=0 Choose not to write out timestamps
    • --log-thread=0 Choose not to write threadid out
    • --log-index=0 Choose not to write index # to log
    • --log-function=1 Choose to log which function printed the message
    • --log-fileline=0 Choose not to log which file and line number the message was printed on
    • --log-leveltype=0 Choose not to log the level of the log (debug,warning,info,etc)
  • /