Second part of the Wall-Of-Text started a couple of messages earlier.
Library function naming, and
self/
this initial member
Standard C does not have a mechanism to split interfaces from different libraries used in a program different
namespaces. Symbols can be declared
static, in which case they are only visible in that file scope (as opposed to global -- accessible from other file scopes, separately compiled binaries linked to the same program -- or local), but that's about it. Oh, and some names are strictly speaking reserved for the standard C library; but when programming microcontrollers, we typically use a
freestanding environment (without the standard C library, and some "custom" libraries like Hardware Abstraction Layers of some sort instead), and not a standard "hosted environment" C programmers are used to.
To avoid problems with different libraries using the same names, many of them prepend their name to all public (or global) variables or functions they export. For example, all functions provided by the
GNU Scientific Library begin with the prefix
gsl_.
In C++, class member functions are provided with a pointer to the object they were called using,
this . In Python, this reference is
self. In general, some variant of
this/
self is provided by all object-oriented programming languages. Because C does not have it, we need to pass it explicitly. Typically, we pass it as the first parameter, because that is easy to remember for us humans, and it allows preprocessor magick if one so desires.
Because I work a lot with numerical data, and occasionally matrices, and the current scientific library interfaces are, uh, rather
arcane (see e.g.
LAPACK six-letter function naming), and I am most interested in providing tools for other people to implement their ideas with, I developed a very simple interface for matrix operations. I've shown a compilable example
here.
Essentially, there is a single matrix object type,
matrix, that describes both first-class rectangular matrices, and "views" to existing matrices. The actual data exposed by a
matrix exists within a reference-counted
struct owner. This means that you can expose the same data in any number of different matrices, transposed or reflected or even just picking an arbitrary regular submatrix, and the change in one is immediately visible in all matrices using the same
struct owner. As long as one remembers to call
matrix_free(&m) on each
matrix m they no longer need, the data in
struct owners is managed and freed only when no longer needed.
(A secondary deletion scheme is also possible,
allocation pools. This means that instead of tracking every single matrix, you create a new allocation pool that you destroy when you are done, destroying all matrices and owner structs in one go. Unfortunately, I discovered that this model is somehow too easy to break by new programmers, so I have dropped it for now. I just wanted to point it out in case anyone wonders about it.)
The important point to notice is that each function is named with a
matrix_ prefix. (Naming this library something less generic, and using that name instead of such a generic name, would be even better, as it would reduce the possibility of name collisions. Who knows, someone might have written another matrix library, and someone might wish to use them both at the same time.)
The first parameter is always a pointer to the matrix to be operated on. While these particular matrices are typically declared as
matrix m;, we cannot pass the matrix itself by value, because then any changes to
m would be only visible within the function, and not to the caller! In C, if we want to modify
m in a function with the change visible in the caller, we must pass a pointer to it. (Note that
matrix *const m means that
m is a
const pointer to a
matrix; i.e. that the pointer
m will not be modified, but the
matrix it points to may be modified. This notation may seem odd at first, but I use it because it tells us human programmers how the function intends to access the pointer. Current compilers can usually infer that kind of "const"-ness automatically, but at least in theory, it can also help the compiler to generate better code.)
What does this mean in practice, then?
There are a number of libraries with both C++ and C
language bindings. If the C++ interface looks something like
LCD mydisplay(parameters...);
mydisplay.print("Hello world!");
then, according to my description of function naming and passing the "object" as the first parameter in C, the C interface is probably either
lcd *mydisplay = lcd_init(parameters...);
lcd_print(mydisplay, "Hello world!");
or
lcd mydisplay;
lcd_init(&mydisplay, parameters...);
lcd_print(&mydisplay, "Hello world!");
depending on whether the
LCD/
lcd type is intended to be dynamically allocated or not; it is basically an arbitrary choice for the library developer to make.
In a lot of C code, fully uppercase names are "reserved" for preprocessor macros. It is just a programmer convention, but a common one; and that is the only reason I used lowercase
lcd for the type name. In other words, it is a common stylistic detail that might help in understanding existing code.
Obviously, not every library has both C++ and C bindings. In fact, only libraries originally written in C tend to have both C++ and C bindings, because C++ is a higher-level language and requires a runtime component anyway.
Unfortunately, this also means that you cannot just take a C++ Arduino library, and expect it to magically work in C. The two are completely different languages. For someone who can program in both languages, it may be anything from trivial to impossible to port a library between the two languages. Fortunately, Arduino libraries, especially display management libraries, tend to fall on the "easy/straightforward" sector, as only a small subset of C++ features are used in Arduino.