How to write an Arduino library with a "singleton" object?

I’m trying to write an Arduino library to interface to a shield I designed, and I would like to imitate many of the libraries out there that simply “work”. By this I mean, for example, you just do “Wire.begin()” or “Serial.begin(9600)” without having to declare an instance of an object or anything.

I wrote a very simple skeleton and tried to compile it but was not successful. I’m still getting used to C++, and I know that there is the → operator as well, but I don’t fully understand how that one works, and given that most libraries out there can be used with the dot notation, I’m not sure what I’m doing wrong. From my code you can see what I’m trying to accomplish.

One thing I also tried was to add “static” to the methods in both the header and the cpp file, but this did not change anything. I also tried looking at source code from a few existing libraries but I couldn’t deduce what they were doing differently from me.

Here is a minimal “non-working” example:

Main sketch:

#include "mylib.h"

void setup() {
  mylib.begin();
}

void loop() {
  mylib.dostuff();
}

Library header:

#include <Arduino.h>

class mylib {

  void begin();
  void dostuff();

}

Library code:

#include "mylib.h"

void mylib::begin() {
  pinMode(LED_BUILTIN, OUTPUT);

  // blink the led a few times
  for (int i = 0; i < 5; i++) {
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
  }

  digitalWrite(LED_BUILTIN, LOW);
}

void mylib::dostuff() {
  // blink the LED
  digitalWrite(LED_BUILTIN, HIGH);
  delay(200);
  digitalWrite(LED_BUILTIN, LOW);
  delay(800);
}

This won’t compile, and the errors are:

C:\Users\fmillion\src\arduino_lib_test\arduino_lib_test.ino: In function 'void setup()':
arduino_lib_test:4:8: error: expected unqualified-id before '.' token
   mylib.begin();
        ^
C:\Users\fmillion\src\arduino_lib_test\arduino_lib_test.ino: In function 'void loop()':
arduino_lib_test:8:8: error: expected unqualified-id before '.' token
   mylib.dostuff();
        ^
exit status 1
expected unqualified-id before '.' token

Thank you!

To make a singleton, you just make the constructor private, and delete the copy constructor/assignment. Then you create a static getter for the singleton instance. The getter is inside of the class, so it has access to the private constructor.

Most Arduino libraries also create a global reference or instance of the class.

For instance:

#pragma once

class MyLib_ {
  private:
    MyLib_() = default; // Make constructor private

  public:
    static MyLib_ &getInstance(); // Accessor for singleton instance

    MyLib_(const MyLib_ &) = delete; // no copying
    MyLib_ &operator=(const MyLib_ &) = delete;

  public:
    void begin();
    void doStuff();
};

extern MyLib_ &MyLib;
#include "MyLib.h"
#include <Arduino.h>

void MyLib_::begin() {
  pinMode(LED_BUILTIN, OUTPUT);

  // blink the led a few times
  for (int i = 0; i < 5; i++) {
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
  }

  digitalWrite(LED_BUILTIN, LOW);
}

void MyLib_::doStuff() {
  // blink the LED
  digitalWrite(LED_BUILTIN, HIGH);
  delay(200);
  digitalWrite(LED_BUILTIN, LOW);
  delay(800);
}

MyLib_ &MyLib_::getInstance() {
  static MyLib_ instance;
  return instance;
}

MyLib_ &MyLib = MyLib.getInstance();
#include "MyLib.h"

void setup() {
  MyLib.begin();
}

void loop() {
  MyLib.doStuff();
}

Do keep in mind that singletons and other global instances are often considered bad practice, but they can be useful when dealing with things like hardware peripherals, where you know you’ll always have a single instance that has to be shared.

The arrow operator or member access operator for pointers (->) is used for pointers (and other pointer-like objects like iterators). To access the members of an object, you use the member access operator for objects (.). To access members of a namespace (including static functions of class), you use the scope resolution operator (::).
Serial.println(); // access member of Serial object
Serial *ptr = &Serial; // pointer to Serial
ptr->println(); // access member of pointer to object
struct MyLib {
  static void some_static_function() {}
};
MyLib::some_static_function(); // access static function
namespace Namesp {
void some_function() {}
}
Namesp::some_function(); // access namespace member

Pieter

I've also seen similar results by:

  1. Making constructor, copy constructor, assignment all private
  2. Making all data members and function members static
  3. Declaring, but not defining, an 'extern' instantaneous of the class: extern myClass myObject;

Then the static members may be accessed by:

myObject.memberFunction();

I'm not sure if any of the items in Step #1 above need to be delete(d) in addition to being private. There may be some subtle difference in this use.

gfvalvo:
I'm not sure if any of the items in Step #1 above need to be delete(d) in addition to being private. There may be some subtle difference in this use.

AFAIK, there are just two differences:

  1. If you make them private without deleting them, you could still use them inside of the class's methods (which is probably a bug).
  2. If you delete them publicly, you'll get an error saying "use of deleted function ...", whereas if you make them private, you get "... is private within this context". Personally, I prefer the "use of deleted function" error, because it clearly shows that the function should never be used.

gfvalvo:
I'm not sure if any of the items in Step #1 above need to be delete(d) in addition to being private. There may be some subtle difference in this use.

Since accessibility is checked before modifiers, "delete" will give a more verbose error log by the compiler instead of generic private accessibility log. That's the only different.