Question

I have no experience with C (only C++ and higher level languages). Right now I have tried and failed to find general guidelines on how to write good C code in a way that allows to separate the programm logic clearly into replacable parts that can be combined with other parts or reused in other projects.

To give some context:

I will have two parties communicating over some channel (e.g. TCP or over a serial port, etc.) that both have to perform computations on the received messages and send their results back and forth many times. Both the exact nature of the computations and the type of communication may need to change. For this reason, I want to separate the communication and peripherals part from the computation-logic part into "modules" (whatever that might mean exactly in terms of the implementation), which I will call "peripheral module" and "protocol module". The communication is assymetric; I will call the parties "Master" and "Slave", which (I think) should both be implemented as the same single compiled programm, selecting Master/Slave behaviour as specified by user input.

The kind of thing I'm imagining is as follows: The "Master" process starts in its "peripheral module", which establishes a connection with the "Slave" process and initializes/fetches some data. It passes the data to the "protocol module", along with (as general as possible) instructions on how it can send messages to (and receive from) the "Slave Process". The protocol module takes over and sends messages to the "Slave Processes" where they are received by its "protocol module" and communication continues. During execution the "protocol module" should be able to send debug/status information back to the "peripheral module", which may or may not run as a separate thread (I currently don't think that is absolutely necessary). Both number, length and structure of messages sent depends on the "protocol module", as does the exact nature of the status/debug information it can provide. There may be several different versions of the "peripheral module", designed to run on different OS and possibly also integrated devices, that should all be able to use any "protocol module" implementation. There may also be several different implementations of the "protocol module".

My current implementation ideas:

Make a header file "interface.h" that acts as an interface between "protocol module" and "peripheral module" that all implementations of each module should conform to. This header file declares functions (all with external linkage) that look something like this (returning error codes with meanings also defined in "interface.h"):

  • int recieve_data(char* data, int num_bytes)
  • int send_data(char* buffer, int num_bytes)
  • int status_info_output(char* buffer, int num_bytes)
  • int begin_protocol(char* initial_data, int num_bytes, SOME_STRUCT config)

The methods recieve_data, send_data and status_info_output are defined by "peripherals.c" and called from "protocol.c", while begin_protocol is defined by "protocol.c" and called from "peripherals.c". The module "protocol.c" will have global variables defining configuration parameters. (With static linkage and perhaps visible access methods, declared in a "protocolXY.h" hearder file that is unique to each "protocol module" implementation).

The issue is where and how to defined SOME_STRUCT, since its exact content will be different (with large overlaps) for different implementations of "protocol.c". Also the "protocol module" will define message headers containing information that needs to be monitored during programm execution. I'm not sure if the status_info_output is versatile enough to provide such functionality. I'm also not sure how to inlude status messages and commands such as "the user interrupting communication during execution" in a way that is cleanly communicated to the other party and can be implemented independently of the "protocol module" (i.e. will most likely not differ between different implementations of it).

What bothers me further is that, from a design perspective, the "protocol module" could be viewed as some sort of static library (it doesn't have to be linked as such). I have never seen library headers demanding from the user (the "peripherals module") to define certain functions. I considered a scheme where begin_protocol is passed function pointers to tell it how to communicate, but this seems more convoluted and perhaps bad for performance optimization (which may be important for the integrated system).

Can anyone provide some advice on how to structure such a project in C and what common practices are in writing modular programms (and perhaps libraries)?

Thanks in advance!

Was it helpful?

Solution

If you are fine with having to create a new executable when you want to use a different peripheral implementation, then your design is mostly good. In C it is not uncommon to have one header file specify an interface and then have multiple source files implementing it in different ways, where the build system decides which source file gets used.

What I would recommend to do differently is also the area where you have the most doubts. In my opinion, the peripheral layer/module should be only responsible for getting bytes across the communication link to the other side and to receive them there.

If you want the Master node to be responsible for choosing the protocol that will be used over the communication link, that is not something that belongs in the peripheral module. It would be a better design to have both modules start up with a "protocol selection" protocol. Then the Master communicates to the Slave using the "protocol selection" protocol what protocol to use for the rest of the communications and both devices activate or switch over to that protocol. This way, a management layer that knows about all available/supported protocols can handle the initialization.

Licensed under: CC-BY-SA with attribution
scroll top