Creating a library: Using classes or not?

I hope so to.:slight_smile:

I will try tomorrow.

That is obviously subjective. Do you have evidence to back your opinion?

Namespace. A class keeps all of your library’s stuff out of the global namespace. A duplicate symbol error during linking will definitely be a burden to inexperienced programmers.

No real evidence, but that’s my feeling as a teacher.

My function naming is quite specific, but that could be a useful advantage indeed.

How many Arduino users have struggled with Serial.available when Serial_available is a reasonable alternative?

Hint: Even a five year old can be taught the purpose of the shift key.

True, you convinced me of using classes :slight_smile:
But I now have a question about the constructor. I understand this is a little like the “setup” section when creating an instance of the class and can be used to specify pin connections, but I was wondering if I should use it for other initialization as well.
See my most recent post about this here

Thanks for the info!

If I were in a class learning C++, I would want to learn about using classes.
Sure you can make a function into a library file with practically no new code, but is this something a programmer would see in the real world?

Learn to do it the hard way today. Everything else will be easy. Prepare the student for the real world so that when they need to write a library for hardware they will have some experience.

Since 99% of embedded development is in C… Yes.

@UKHeliBob and @pert nailed it. Include an initialization method. begin is a great choice for the name. Document that begin has to be called; ideally in setup.

You can also look at this thread for creating a library. Post #5 from @gfvalvo describes the standard model for composing the .cpp and .h files.

Thanks for your responses everyone! @6v6gt, I’ll check that thread out tomorrow. I just read it very quickly but I might have some questions about it if you don’t mind :slight_smile:

Check out my tutorial on How to Write Your Own Arduino Libraries which goes through step by step, splitting out your methods into separate files and then creating a class if you need to.
You really don’t need a class unless
a) you are going to have more than one instance of the methods
b) you what a general re-usable library for other project.

1 Like

Hi @drmpf, thanks for the link, I’ll look into it for sure!

That’s what I thought. For my project it’s useless to create more than one instance of the class, because that one instance will cover all present hardware. And the library is intended specifically for this project alone, to make controlling some of the more advanced hardware a little easier for the students.

That said, the “namespace” thing that Coding_Badly mentioned is an advantage, and it might be good practice for the students to learn early on how to use component libraries using classes. That way they will recognize the structure when they use, for example, the “servo” library for the first time.

Feel free to tell your view on this!

While I can see the necessity of namespaces in some instances (like the clash between mbed / Arduino Stream), as an Arduino library writer I find their use in some board packages a nuisance as I have to add extra ifdefs to include/exclude the namespace in my libraries to suit the various different usages in different board packages.

Most third party libraries, including mine, don’t use them.

I’m not going to get too deeply involved in a debate about the relative merits of classes and namespaces but I do tend to use namespaces. In coding terms, and when anyway only one instance of a class would exist, the difference is minimal. However, the end user of a namespace is exposed to the scope resolution operator ‘::’ which may take some explaining.

I’d simply say this. If you can reduce your code to a single function, with its global footprint reduced to the minimum (make use of static state variables to help achieve this), then design it a single function and let the students simply copy it into their own programs.

If if is more complicated and requires multiple functions (methods), then use a class model.
Since only one instance of the class will be used in this case, you can instantiate it already in the .cpp file. A class can also exist in the same directory as the sketch. No special loading into the Arduino library directory is necessary.

I could imagine that the students will, anyway, have to get used to handling classes if they want to start using peripherals or modules, even say an LCD display.

My suggestion is not to use C++ namespaces but to use a C++ class to provide the same features afforded to using a namespace.

I apologize for the confusion.

I am approaching my answer above as a student. I’ve been writing code for my Arduinos for three years, and learning C++ the hard way. In all honesty, I don’t know the difference from C++ namespaces and C++ classes. As a student, I would like to be introduced to these concepts early and as simply as possible.

So, if you have a function to turn on and off an LED, teach the different ways it can be represented. A simple function or as a library. This way when the student encounters a more complex library it won’t be complete gibberish to them. As it is to me.

1 Like

I would like to add a link from Sparkfun:

additionally: I find it very common to make a project with one LCD , one temperature sensor, one pressure sensor … all single objects of separate classes. So why not to go OOP and use classes to encapsulate data and methods even if you think that only one instance will be needed in future?

One other option for an OOP solution even when there will only be one object instantiated is to force that object to be a Singleton. This gives you the nice encapsulation and abstraction of a class and ensures that the user won’t be mucking about in your namespaces. The example below is a variant on the technique @PieterP posted here. The library provides a single instance of the class. Because the constructor is private, and the copy-constructor and operator= are deleted, it’s impossible for the user to instantiate any others.

MySingletonClass.h:

#ifndef MYSINGLETONCLASS_H_
#define MYSINGLETONCLASS_H_

#include "Arduino.h"

class MySingletonClass {
public:
	MySingletonClass(const MySingletonClass &) = delete; // no copying allowed
	MySingletonClass &operator=(const MySingletonClass &) = delete; // no assignment allowed
	static MySingletonClass &getInstance(); // Accessor for singleton instance

	void examplePublicInstanceFunction();
	static void examplePublicClassFunction();

	uint8_t examplePublicInstanceVariable {0};
	static uint8_t examplePublicClassVariable;

private:
	MySingletonClass();  // Constructor is private
	void examplePrivateInstanceFunction();
	static void examplePrivateClassFunction();

	uint8_t examplePrivateInstanceVariable {0};
	static uint8_t examplePrivateClassVariable;
};

extern MySingletonClass &singleton;

#endif /* MYSINGLETONCLASS_H_ */

MySingletonClass.cpp:

#include "MySingletonClass.h"

uint8_t MySingletonClass::examplePrivateClassVariable { 0 };
uint8_t MySingletonClass::examplePublicClassVariable { 0 };

MySingletonClass::MySingletonClass() {  // Private constructor
}

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

void MySingletonClass::examplePublicInstanceFunction() {
}

void MySingletonClass::examplePublicClassFunction() {
}

void MySingletonClass::examplePrivateInstanceFunction() {
}

void MySingletonClass::examplePrivateClassFunction() {
}

MySingletonClass &singleton {MySingletonClass::getInstance()};

The main .ino file uses the one (and only possible) instance of the class named “singleton”:

#include "Arduino.h"

#include "MySingletonClass.h"

void setup() {
	singleton.examplePublicClassVariable = 100;
	singleton.examplePublicInstanceVariable = 200;

	singleton.examplePublicInstanceFunction();
	singleton.examplePublicClassFunction();
}

void loop() {
}

One of the main advantages of encapsulation using classes (singletons or otherwise) is discoverability: When you type class_instance. a decent IDE with autocomplete (including the Arduino IDE 2.0) will give an overview of the different methods and accessible members of the class. This makes it very easy for new users to discover what functionality is available without having to switch to the documentation every 10 characters.

Free functions are harder to use with autocomplete because it doesn’t narrow down the list of suggestions for a given context. There are languages with uniform function call syntax, which solves exactly this dilemma between writing classes or free functions. There have been multiple proposals to add it to C++, but none of them got standardized yet.

I’d like to point out that choosing between classes and free functions does not have to be mutually exclusive: it often makes sense to implement certain logic and algorithms as free functions, and then glue them together into a coherent interface using a class.
For example:

namespace detail {
int *binary_search(int *begin, int *end, int target) {
  ...
}
} // namespace detail

class SomeContainer {
  private:
    int storage[100];
    size_t size = 0;
  public:
    void insert(int) { ... }
    void remove(int) { ... }
    int *find(int target) { 
        return detail::binary_search(storage, storage + size, target);
    }
};

Using free functions like this can greatly improve code reuse and testability.
Note that I used a namespace to avoid polluting the global namespace with dozens or hundreds of free functions, as to not contribute to the autocomplete problem mentioned above.

If this meant to be an educational library, it might be a good idea to gradually expose the students to all three concepts (free functions, namespaces and classes).

Singletons are certainly useful in some scenarios, but you have to be careful not to overuse them. For example, someone writing code for an Arduino Uno might be tempted to make the class for the Serial port a singleton, because the Uno only has one. But then he/she might switch to an Arduino Mega, which has multiple serial ports, and it turns out that a singleton wasn’t the best idea, and a “normal” non-copyable class with predefined global instances would have been a better design choice. (Which is exactly what the Arduino developers opted for.)

As a final note, I believe it’s good practice to wrap your entire library in a namespace. It prevents name collisions, both at compile time and at link time. There are plenty of libraries that define a global enum { Off, On }, for example. Generic names like that are bound to collide with other libraries.
If you want, you can hide the namespaces from your users as follows:

// MyClass.hpp
namespace mylib {
class MyClass { ... };
} // namespace mylib
// MyLibrary.hpp (main header that users will include in their Arduino sketch)
#include "MyClass.hpp"

#ifndef MY_LIBRARY_NO_USING_NAMESPACE
using namespace mylib;
#endif

This makes the namespace invisible to beginners who might not expect a namespace in an Arduino library, but it does prevent name collisions with other libraries. If there are any collisions or ambiguities, these can easily be resolved by specifying the namespace explicitly, or by defining the MY_LIBRARY_NO_USING_NAMESPACE macro before including your library.
Without namespaces, there is no way to resolve the ambiguities except modifying the library source files or rewriting your code to never use the two libraries in the same sketch/file (which is often not an option, and even then you might end up with problems at link time).

1 Like