configure library at the compile time

If you have a library that you would like to reuse across projects with different hardware or need to support multiple hardware revisions with changing signal pin assignments, you know what this is about. There is several ways I identified so far:

1. library code is entirely in header file

This is frowned upon by C++ programmers as poor coding practice:

project_path/sketch.ino

#define __MAIN__
#include <mylibrary_config.h>
#include <mylibrary.h>
// ...

project_path/mylibrary_config.h

#define MY_PIN 2

library_path/mylibrary/mylibrary.h

#ifndef mylibrary_h__
#define mylibrary_h__ 1
// ... declarations
#ifdef __MAIN__
// ... code using MY_PIN from mylibrary_config.h
#endif // #ifdef __MAIN__
#endif // #ifndef mylibrary_h__

2. extern const

This seems to be preferred method by C++ folks as it does not use preprocessor but C++ features, but I find Arduino does not optimize as well as in case 1 above and produces larger code

project_path/sketch.ino

#include <mylibrary.h>
extern const int MY_PIN = 2;
//...

library_path/mylibrary/mylibrary.h

#ifndef mylibrary_h__
#define mylibrary_h__ 1
// ... declarations
extern const int MY_PIN;
#endif // #ifndef mylibrary_h__

library_path/mylibrary/mylibrary.cpp

#include "mylibrary.h"
// source code using MY_PIN from sketch.ino

3. library configuration header

This seems to be preferred method in embedded development, but Arduino default install does not support it. It is possible by adding -I{build.source.path} in platform.txt before or after {includes}. When used before, the project files may overshadow same-named header files in libraries. I recommend placing it after, so project directory will be searched last.

project_path/sketch.ino

#include <mylibrary.h>
//...

project_path/mylibrary_config.ino

#define MY_PIN 2
//...

library_path/mylibrary/mylibrary.h

#ifndef mylibrary_h__
#define mylibrary_h__ 1
#include <mylibrary_config.h>
// ... declarations
#endif // #ifndef mylibrary_h__

library_path/mylibrary/mylibrary.cpp

#include "mylibrary.h"
// source code using MY_PIN from mylibrary_config.h

The ideal (best optimized) method are global #defines, but I couldn’t figure out yet how to define these project-wide in the IDE. Adding them to the main source file can help only if the library is entirely contained in the library header file. Else such a #define will be used by the compiler only when compiling just that file, but not when compiling the library cpp files.

External constants are less efficient, because they have to be evaluated at runtime, with little chances for compiler optimizations.

The most practical method seem to be local copies of the libraries, as currently generated by the IDE when a library file is opened in a new tab and edited. This will make the projects insensitive to different edits in other projects.

  1. This is the technique I've settled on as the only usable solution but I dislike having to put so much of the code in the header file.

  2. In my case I specifically needed to use the preprocessor so this wouldn't be useful for my application.

  3. I don't consider this feasible because I can't expect users to modify the platform.txt of every core they use the library with and repeat the process every time they update to a new version of a core. The key would be to allow something like this to be done without needing to modify the cores but the Arduino developers have refused to consider any variation of this concept because not having it "forces the library developers to make a more user friendly API". Well that kind of makes sense, except sometimes there's just no alternative. I don't consider requiring the library to be edited every time the configuration needs to be changed user friendly at all.

DrDiettrich:
The most practical method seem to be local copies of the libraries, as currently generated by the IDE when a library file is opened in a new tab and edited. This will make the projects insensitive to different edits in other projects.

The problem is any time you want to update the library you need to replace every copy.