I'm working on projects where I write code on both the client and supplier side.
Consider two distinct projects: Project A, which utilizes Chip A, and Project B, which utilizes Chip B. Both Chip A and Chip B offer similar core functionalities.
I have encapsulated the functionalities of Chip A and Chip B into separate modules, namely chip_a and chip_b. The header files for these modules are structured as follows:
chip_a.h
typedef sturct
{
uint8_t element1;
uint8_t element2;
...
uint8_t element8;
uint8_t element9;
}chip_a_t;
bool chip_a_get_element1(chip_a_t* me);
bool chip_a_get_element2(chip_a_t* me);
...
bool chip_a_get_element8(chip_a_t* me);
bool chip_a_get_element9(chip_a_t* me);
chip_b.h
typedef sturct
{
uint8_t element_a;
uint8_t element_b;
...
uint8_t element_y;
uint8_t element_z;
}chip_b_t;
bool chip_b_get_element_a(chip_b_t* me);
bool chip_b_get_element_b(chip_b_t* me);
...
bool chip_b_get_element_y(chip_b_t* me);
bool chip_b_get_element_z(chip_b_t* me);
Now, suppose my program logic in program.c requires a specific value, element_123. The way this value is obtained differs based on the chip being used:
Crucially, the code in program.c that utilizes element_123 should remain the same across different projects. The only variable is the choice of the underlying chip.
program.c
agent_t agent = {0};
if (agent_get_element_123(&agent) != true) {return;}
uint8_t data = agent.element_123 + some_data;
/*do other things...*/
Currently, my implementation of agent.c is as follows:
/*agent.c*/
bool agent_get_element_123(agent_t* me)
{
chip_a_t chip_a = {0};
if ((chip_a_get_element1(&chip_a) != true)
|| (chip_a_get_element8(&chip_a) != true))
{
return false;
}
me->element_123 = chip_a.element1 + chip_a.element8;
return true;
}
As it stands, the agent module is tightly coupled to the chip_a module, making it suitable only for Project A. To use Chip B in Project B, I would need a separate version of agent.c that interacts with chip_b.
My question is: Is there a way to write the agent.c code such that it can be used seamlessly across different projects (which might utilize either Chip A or Chip B) without requiring any modifications to program.c?
Additional Information:
There are SO many ways to handle stuff like this, a lot of it depends on what exactly you want to do and what your limitations are.
For example, is the interface between the chips identical, so that the code in agent.c is the same, just with different function names? Then you can just write all your chips to have the same API and link in whichever one you want.
If the code is different, is it possible to introduce a higher-level interface that provides a similar API across all the chips? Then you can have each chip provide this interface and write agent.c to that interface.
You can have an array of function pointers, and each chip fill it in with their functions, then agent.c would choose the element in that array and call the function.
You can create multiple shared libraries, one for each chip, then have agent.c use dlopen() to pick the right one and dlsym() to obtain pointers to the functions.
Do you want the choice of which chip to be used to be a compile time decision (use a preprocessor option to choose), a link time decision (link in different object files with the same API), or a run time decision (choose different share libraries at runtime)?