oF code style

tpltnt edited this page Jul 23, 2015 · 46 revisions

The openFrameworks code style, modeled after the Qt style

Indentation

  • Use tabs for indentation (not spaces) and set your tab width to 4 spaces. Keep in mind the Github source code viewer uses a tab width of 8 spaces.
  • No variable and comment alignment since this is dependent on tab width
// Wrong
int    height;      // Comment A
float  scaleFactor; // Comment B
string name;        // Comment C

// Correct
int height; // Comment A
float scaleFactor; // Comment B
string name; // Comment C
  • Indent blocks inside preprocessor definitions
// Wrong
#ifdef WIN32
#define IS_WIN32
#endif

// Correct
#ifdef WIN32
    #define IS_WIN32
#endif
  • The public, protected, and private keywords should be indented inside the class with the function declarations indented as well.
 // Wrong
 class myClass{

 public:
     myClass();

     void aFunction();
 };

 // Correct
 class myClass{

     public:
         myClass();

         void aFunction();
 };

Declaring variables

  • Declare each variable on a separate line
  • Avoid short (e.g. a, rbarr, nughdeget) names whenever possible
  • Single character variable names are only okay for counters and temporaries, where the purpose of the variable is obvious
  • Wait when declaring a variable until it is needed
// Wrong
int a, b;
char * c, * d;

// Correct
int height;
int width;
char * nameOfThis;
char * nameOfThat;
  • Variables and functions start with a lower-case letter. Each consecutive word in a variable’s name starts with an upper-case letter (camel case).
// Wrong
int thisisanimportantinteger;

// Correct
int thisIsAnImportantInteger;
  • Avoid abbreviations
// Wrong
short Cntr;
char ITEM_DELIM = '\t';

// Correct
short counter;
char itemDelimiter = '\t';

Class Naming

OF Core class names start with "of". eg ofTexture. Addon authors are encouraged to use the "ofx" prefix.

// Wrong
class OFCoolClass{
...

// Correct
class ofCoolClass{
...

Whitespace

  • Use blank lines to group statements together where suited
  • Always use only one blank line
  • Never use a space between a keyword and an opening curly brace or between a closing parenthesis and an opening brace.
// Wrong
if (foo) {
    // do something
}

for ( int i = 0; i < 10; ++i ) {
    // do something
}

// Correct
if(foo){
    // do something
}

for(int i = 0; i < 10; ++i){
    // do something
}
  • For pointers or references, always use a single space between the type and * or &, and a space between the * or & and the variable name.
// Wrong
char* x;
const QString &myString;
const char* const y = "hello";

// Correct
char * x;
const QString & myString;
const char * const y = "hello";
  • Surround binary operators with spaces
// Wrong
int a = b+c;

// Correct
int a = b + c;
  • No space after a cast
// Wrong
char* blockOfMemory = (char* ) malloc(data.size());

// Correct
char * blockOfMemory = (char *)(malloc(data.size()));

Braces

  • As a base rule, the left curly brace goes on the same line as the start of the statement. Do this with function implementations and class declarations. No Exceptions
// Wrong
if (codec)
{
    // do something
} 

// Correct
if(codec){
    // do something
}

// Wrong
static void foo(int g)
{
    qDebug("foo: %i", g);
}

// Correct
static void foo(int g){
    qDebug("foo: %i", g);
}

// Wrong
class Moo
{
    public:
        // stuff
};

// Correct
class moo{
    public:
        // stuff
};
  • Always use curly braces for all conditional statements and single line statements.
// Wrong
if (address.isEmpty())
    return false;

// Correct
if(address.isEmpty()){
    return false;
}

// Wrong
for (int i = 0; i < 10; ++i)
    qDebug("%i", i);     

// Correct
for(int i = 0; i < 10; ++i){
    qDebug("%i", i);
}

Parentheses

  • Use parentheses to group expressions:
// Wrong
if(a && b || c){
    // do something
}

// Correct
if((a && b) || c){
    // do something
}

// Wrong
int d = a + b / c;

// Correct
int d = (a + b) / c;

Switch statements

  • The case labels are indented from the switch statement
  • Every case must have a break (or return) statement at the end or a comment to indicate that there’s intentionally no break, unless another case follows immediately.
switch(myEnum){
    case Value1:
        doSomething();
        break;
    case Value2:
    case Value3:
        doSomethingElse();
        // fall through
    default:
        defaultHandling();
    break;
}

Line breaks

  • Keep lines shorter than 100 characters; insert breaks if necessary.
  • Braces go at the end of a broken line; operators start at the beginning of the new line. An operator at the end of the line is easy to not see if your editor is too narrow. Indent each line one indent inside the conditional statement
// Wrong
if (longExpression +
    otherLongExpression +
    otherOtherLongExpression){
    // do something
}

// Correct
if(longExpression
    + otherLongExpression
    + otherOtherLongExpression){
    // do something
}
  • There is no line break between a closing curly brace and an else statement
// correct
if(...){
    ...
}else{
    ...
}

//wrong
if(...){
    ...
}
else{
    ...
}

Comments

  • Comments have a space after the '//'
// Wrong
//this is a comment

// Correct
// this is a comment
  • Do not use multi-line comments. Yes, they save time, but make it a pain to comment out large sections of code when debugging.
// Wrong
/*
   Yo.

   This is
   a multiline comment.

   Peace.
*/

// Correct
//
// Yo.
//
// This is not
// a multiline comment.
//
// Peace.
//

Important OF-specific considerations

Clear vs Clever

We prefer clear code to clever code - ideally it can be both.

Commenting

  • Clearer code is better than explaining comments.
  • Avoid redundancy; comment on the "why", not the "what".
  • If code is changed, keep the comments up-to-date.
  • Guidelines on Doxygen/automated comments are still pending.

Logging

  • Never use printf or cout, use ofLog.
  • Warnings, Errors, FatalErrors, and Verbose log level messages must have the name of the calling module (basically class name) in their header. This way users have more info as to where the issue may be happening.
// ie inside ofImage.cpp

// Wrong
ofLog(OF_LOG_WARNING, "couldn't load image");

// Correct
ofLog(OF_LOG_WARNING, "ofImage: couldn't load image");
  • Notice level messages may or may not have the module header, depending on usage. Use your best judgement.

Ternary operators

Don't use ternary operators (i.e. result = a > b ? x : y;). This is only clear to super nerds. Are you a super nerd?

While loops

Don't use while loops if possible.

Iterators vs for loops

Use for loops for simple cases and iterators for more complex iteration.

C arrays vs vectors

Use vectors unless there is a strong reason not to.

Const correct / adding const

When developing new code, you are encouraged to use const arguments and const methods wherever possible with the exception of return types. If you need a const return type, you must also provide a non-const version. From the user's perspective, we encourage const guarantees, but not const requirements.

In general, do not create a situation that forces the user to be aware of const.

We currently prefer that const is only added to existing code as needed, in accordance with the above guidelines.

Initialization lists

We don't do this in the core. If you need it for initializing a reference then use it but otherwise do your initialization in the constructor.

Initialization member variables

  • Always initialize variables with sensible starting values in the class constructor.
  • Pointers should be initialized to NULL.

Assert

Please don't use assert. We will find you and and make you pay! :)

Exceptions

Please don't use exceptions. Print a warning or an error and clean things up instead. If you are using a library that uses exceptions, make sure to catch and print the errors so exceptions are not thrown in user code.

Constructors that take arguments

  • Only do this if there is another way to set the starting params of a class.
  • We prefer setup or load functions for larger classes.
  • For classes like ofPoint constructor args make sense but also provide a set() method which does the same thing.

//TODO:

Please use // TODO: comments if you find something that needs fixing later. eg: // TODO: fix this as its not returning a value.

Class method order

  • We try and order the methods and member variables in the header file in the order that they should be used, and from least-technical to most-technical. For example:

    • constructor / destructor
    • initialization and setup related functions
    • core functionality
    • operator overloading
    • member variables
  • This ordering also applies to non-class headers. For example, an overloaded function taking drawStuff(int x, int y) should precede drawStuff(ofPoint& point) function.

Naming conventions

  • We use the 'of' prefix for global functions, and class names. eg ofImage, ofSetColor, etc.
  • We use non of prefix for class methods eg: ofImage::draw() instead of ofImage::ofDraw.
  • Methods that modify their object are named like ofColor::normalize(), while methods that return a copy are named like ofColor::getNormalized.

  • Most importantly - please look at how other similar things in oF are named, e.g. if ofGetSomething, ofEnableSomething, ofDisableSomething are used. Use your judgment to name things in a way that will be intuitive to others (not just yourself) based on the current oF API.

Namespaces (esp. 'std')

Avoid using namespace std (globally) as disussed in #2581 and #4012 due to potentially introducing sneaky counter-intuitive side-effects via the global namespace. std:: is to be used in core header files. using namespace std; might be ok in (core) .cpp files, but only if it really improves readability (esp. in diffs). Examples and ofApp must not usestd:: as ofMain.h will still include using namespace std;.

Member variable accessibility

  • There should be no public member variables except in very small data passing classes/structs. Write getters/setters for access.
  • Make variables protected for classes that are designed to be inherited. Use your best judgement here and try to expose only those variables which can't mess up the internal workings of the class easily.
  • Make all other variables private and add getters/setters where applicable.

Enums

  • If a function or method returns or takes as input an entry from a selection of options, use an enum instead of int. Make sure the function or method parameter is set to be of the enumtype and not int.
// We don't do this:
#define OF_IMAGE_QUALITY_BEST 0
#define OF_IMAGE_QUALITY_LOW 1

class ofImage{

  …

  void setQuality(int quality);

  …

};

//--------------------------------------------------
// We do this:

enum ofImageQualityType{
  OF_IMAGE_QUALITY_BEST,
  OF_IMAGE_QUALITY_LOW
};

class ofImage{

 …

 void setQuality(ofImageQualityType qualityType);    // use this

 …

};

void testApp::setup(){

 …

 myImage.setQuality(OF_IMAGE_QUALITY_BEST);

 …

}
  • If the enum is tightly tied to a particular class, declare the enum inside the class instead of repeating the class name in the enum name. The above could be more succinctly implemented as this:
class ofImage{

  …

  enum QualityType{
     QUALITY_BEST,
     QUALITY_LOW
  };

  void setQuality(QualityType qualityType);

  …

};

void testApp::setup(){

  …

  myImage.setQuality(ofImage::QUALITY_BEST);

  …

}

Finally - Important Considerations For Contributing Code.

  • Please test all the code you are submitting and all the use cases of it.

  • Include a complete list of changes in your pull request.

  • Always look at the diff tab of the pull request and check that nothing was unintentionally submitted.

    • A good practice is to add your changes using the --patch (or -p) option which lets you go change by change and decide what to do with those (add/ignore/edit). This way you do a final review before adding the changes which is good to prevent undesired/forgotten changes: git add -p
  • Never do git add *. Always add new files one at a time

  • Don't mix formatting / code cleanup commits with commits for bugs and/or features. They will be rejected as it makes it very difficult to see actual api changes.

  • Have the code you write insert into the api as seamlessly as possible. If in doubt match the style / naming conventions of what is already there. Be a code ninja! Leave no trace :)

For the record: Major modifications to the Qt style

  • Removed "Q" class-name prefix, as it makes no sense for oF.
  • Curley Braces always open on the same line as statement, method, class.
  • No capitalization of class names.
  • Dropped the item "Inheritance and the virtual keyword"