Skip to content
alexandre burton edited this page Oct 4, 2023 · 52 revisions

The openFrameworks code style

Two concepts overlap in the "style": (1) whitespace / indentation / bracing and (2) concepts like variable naming.

1. Whitespace / indentation / bracing

To simplify whitespace and bracing management, in 2023 a .clang-format file has been adopted, greatly simplifying the formatting process. The starting point was the 'webkit' template, from which a few tweaks managed to conform to almost all specificities of the current code.

As a result of that, the styling guide has been simplified. The .clang-format itself can be referred to as the whitespace styling reference but should simply be executed to conform the code (there are many ways to execute clang-format, from the command line, within IDE's, or even within git.); the main points :

  • 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 by default; you can change this in your Github Settings.

  • Indent blocks inside preprocessor definitions

#ifdef WIN32
    #define IS_WIN32
#endif
  • The public, protected, and private keywords should not be indented inside the class
 class myClass{
 public:
     myClass();
     void aFunction();
 };
  • Pointers or references as well as binary operators use a space on each side:
Correct Wrong
```c++

char * x; const QString & myString; const char * const y = "hello"; int a = b + c;


</td><td>

```c++

char* x;
const QString &myString;
const char* const y = "hello";
int a = b+c;

  • The left curly brace goes on the same line as the start of the statement.
Correct Wrong
if (codec) {
    // do something
}
if (codec)
{
    // do something
} 

But again, all the above should be mechanically processed by clang-format and should not take specific attention.

NOTE: A decision was made not to "blanket-reformat" the codebase as even though git is not bad in ignoring whitespace, it does cover the history with a "reformat" commit. In order to prevent that, the formatting is applied as other changes are occurring in files, so the (squashed) commit message is relevant to function/implementation and not formatting.

2. Class and variable naming, etc.

These are the things that are not covered by the clang-format operation:

  • 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
// 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';
  • OF Core class names start with "of". eg ofTexture. Addon authors are encouraged to use the "ofx" prefix.
// Wrong
class OFCoolClass{
...

// Correct
class ofCoolClass{
...

* Use parentheses to group expressions:

```cpp
// 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;
  • Keep lines shorter than 100 characters; insert breaks if necessary.

  • 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.
//

Other 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.

Setup and Constructors

  • An object should have a setup (or sometimes set) method that can be reapplied over it's lifetime
  • Complex setups use a Settings object (ofSoundStreamSettings, ofGLWindowSettings, etc)
  • Constructors can be provided but must mimic the setup arguments order
  • Objects should be as functional as possible by default; if an object requires a setup() call to be operational, it should be well documented.

//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.

  • Avoid using namespace std globally.

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);
 …
}