A list of Dos and Don'ts when writing a library and/or class.

So you want to write a library to go with some nice fancy piece of hardware you've created. Excellent.

Before you do, please try not to embarrass yourself.

I have seen many libraries that have been written by Arduino users, and I am not going to point any fingers or name any names, but there have been some shocking pieces of code written.

So, before you start writing your library, please check through this list of dos and don'ts so you can make a library that won't make you look a fool.

Don't:

Don't put all your code in one header file. C code goes in .cpp files, and header information goes in .h files. I have seen a number of libraries which consist of one single .h file which is packed with C code. This is very very bad practice. If your sketch involves more than one file, and you include this header file in each file of your sketch, you will be duplicating the code within that header file. This will not only waste space, but will duplicate any objects and variables defined by the code. The only time it is acceptable to have code in a .h file is when defining a macro. Header files should only contain references to external entities (variables and function defined in C files), class definitions (not their function code - just the definitions) and macro definitions.

Do:

Do include or inherit from other people's libraries. There is no point in inventing the wheel. If your library does the same job as someone else's but with a few differences, just inherit their class and overload the relevant functions. This can be done with system objects, like the Serial object, or the SPI object, for instance. Also, if you are using facilities in your library that are provided by another library, don't duplicate what that library does. Just include that library in your code. A good example is the SPI library. I have often seen libraries which duplicate a lot of the code that the SPI library provides, just because the device the library supports is an SPI based device. This makes libraries very non-portable. There are a wealth of devices around now which use the Arduino (wiring) environment, and they aren't all AVR based. Porting libraries to these platforms is far easier if they don't use any processor specific facilities. Porting the "core" libraries should be enough to then allow other libraries to operate with no changes.

Don't:

Don't re-invent the wheel. If there is an existing library which does a similar job to yours, but with, for example, different hardware, try and match the interface of that library. It makes it far easier for other users to adopt your library if it is basically a drop-in replacement for another library. If you know the interface (function calls, class methods, etc) to one, you know the interface to the other. There is very little more to learn.

Do:

Document your library. In the header file document what each function does, what it expects to be provided with, and what it should return. Also document all the #define macros and how they affect the program, and what effect changing them will have. If your library requires the user to include other libraries in their code (like the SPI library) then make this obvious right at the top of the header file. Also, if possible, write a manual to go with it detailing all the functions and methods and what they all do. Provide some example code to show how to use the different functions.

Don't:

Don't be messy. Code indenting exists for a reason. Make sure that your code is easy for others to read and follow. Don't be afraid of using long variable names that show exactly what they are. Function names should be descriptive and easy to remember.

Do:

Overload system functions. If your library provides some functions that do a similar job to a built-in function, such as analogRead(), or digitalWrite(), overload the system functions but using your own structures as parameters to keep them unique. This way people can continue to use the existing functions they already know. You can #define some symbols to equate to your own structure based variables so they don't have to know about that - something like:

struct myAnalogDevicePin {
  int number
};

#define EXT_A0 (struct myAnalogDevicePin){0}

unsigned int analogRead(struct myAnalogDevicePin pin)
{
  return myClass.readAnalogValue(pin.number);
}

// ...

myReading = analogRead(EXT_A0);

Don't:

Don't be afraid to suggest other best practices, or lambast the ones I have listed here. These are really my own personal opinion, but one built up from many years of programming - both as a hobby and as a job.

The only time it is acceptable to have code in a .h file is when defining a macro. Header files should only contain references to external entities (variables and function defined in C files), class definitions (not their function code - just the definitions) and macro definitions.

I disagree. Code for setters and getters can easily be put in the header file. If all that a getter does is

int Class::GetValue()
{
   return _value;
}

I see no reason not to put that in the header file. If one is looking at the header file to see what the methods are, they can easily see that the function is small, and is not going to be in the source file.

Similarly, if the setter just assigns a value to a field, there is no reason to define the method in the source file.

If the methods do anything significant or tricky, then, I agree that the code goes in the source file.

Code indenting exists for a reason.

Code outdenting, where the curly braces are placed before the statement/function opener should never be used. Unless you want to be laughed at. :slight_smile:

Function names should be descriptive and easy to remember.

They should almost always use both a noun and a get in the name. GetSomething(), ComputeResult(), etc. are way better names than pin(), set(), etc. One should be able to look at a function name, and have reasonable confidence that they know what the function does, and should almost be able to call the function without having to think about the order of the arguments. digitalWrite() is a good example of this. One could just about be certain, without even looking, that the function is going to take two arguments, and what order the arguments are to be in.

Don't be afraid to suggest other best practices

Throw away your first attempt. Submit your second (or later) attempt for review. The first should be considered a proof of concept. Once you know it can be done, do it again and do it right.

The preprocessor (cpp) goes through the cpp file and literally includes the contents of any #include statements.

This resultant file is then compiled.

The compiled (object) file is then linked with other compiled (object) files, along with any other library files, plus a few extra special files, to make the final executable.

Anything that is in a .h file forms part of the c or cpp file that it is included in. Anything that is defined in that header file is thus defined in the C file.

Variables and functions should only ever be defined in a C or CPP file, and the header file just provides pointers that tell the other C or CPP files that those variables exist. It is then down to the linker to join those references up together to the one place where they are defined.

If you define a variable in a header file, then you include that header file in 2 separate c files, you will be defining the variable twice. Not only will it be using twice as much memory, but the two variables will be distinct. Changing one won't change the other. A debugging nightmare.

Anything that is only being used inside the one C or CPP file can be defined in the C or CPP file it is used in and can be omitted in the H file.

Any function definitions in the H file should be defined "extern":

extern void myFunction(unsigned int, unsigned char);

This tells the compiler that the functions might be found in some other C file, not the one it is compiling at the moment.

Also note that it's not necessary to specify variable names at this point, only variable types. It doesn't hurt to name the variables though if it makes it more readable - they basically get ignored anyway.

All variables mentioned in H files should be "extern" as well:

extern unsigned int myVariable;

Otherwise you are going to get multiple definitions.

Class definitions should be in the header file, but the actual class method code should be in the c file:

// Header file:

class myClass {
  public:
    unsigned char property;
  public:
    void myMethod(unsigned int, unsigned char);
};

// CPP file:

#include "myClass.h" // include the definition

void myClass::myMeothod(unsigned int foo, unsigned char bar)
{
  // do stuff here
}

If you want to make a default object for people to use (like Serial and SPI do) then define it in the cpp file, and reference it as "extern" in the header file:

// header
extern myClass MyDevice;

// c file

myClass MyDevice;