Say I have two device drivers and I want them to share the same interface so a caller doesn't know which driver it is talking to exactly. How would I organize this in C? I have thought of a couple of ways:
First: Create a pair of .c/.h files for both drivers with the same interface and create a switch in the caller:
//main.c:
#ifdef USING_DRIVER_1
#include "driver_1.h"
#else
#include "driver_2.h"
#endif // USING_DRIVER_1
Second: Use a single header and create a file-long switch in the drivers' source file like so:
//driver_1.c:
#ifdef USING_DRIVER_1
#include "driver.h"
bool func(uint32_t var)
{
foo(var);
}
#endif // USING_DRIVER_1
//driver_2.c:
#ifndef USING_DRIVER_1
#include "driver.h"
bool func(uint32_t var)
{
bar(var);
}
#endif // !USING_DRIVER_1
Third: This one is a lot like the second one but instead of using switch statements in files themselves, a specific driver is chosen in the makefile or IDE equivalent:
#makefile:
SRC = main.c
#SRC += driver_1.c
SRC += driver_2.c
I'm sure one of these is superior to others and there are probably some I haven't thought of. How is it done in practice?
EDIT:
Details about my particular system: my target is an ARM microcontroller and my dev. environment is an IDE. Device drivers are for two different revisions and will never be used at the same time so each build should contain only one version. Devices themselves are modems operating via AT commands.
All three variants are actually useful. Which to choose depends on what you actually need:
const struct
s which provide the interface (function pointer and possibly other data).#if .. #elif #end
. That makes sense if the two drivers have only minor differences, e.g. different SPI interfaces (SPI1 vs. SPI2 ...). Then this is the way to go. With some effort in the build-tool you can even use this for case 1. (one file for two different drivers, but not my recommendation).Note for all but the first approach, both drivers have to provide an identical interface to the application. The first approach actually allows for differences, but that would actually require the user code treat them different and that's likely not what you want.
Using a single header file for both drivers (e.g.: "spi_memory.h" and "spi_flash.c" vs. "spi_eeprom.c") does ensure the application does not see an actual difference - as long as the drivers also behave identically, of course. Minor differences can be caught by variables in the interface (e.g. extern size_t memory_size;
) or functions (the better approach).