The title may not be clear so I'll give an example.
I am trying to make a system of "data streams" in C.
Type STREAM
:
typedef struct {
void (*tx) (uint8_t b);
uint8_t (*rx) (void);
} STREAM;
I have a file uart.h
with uart.c
which should provide a STREAM
for UART.
I decided it'll be best to expose it as a pointer, so it can be passed to functions without using ampersand.
This is the kind of functions I want to use it with (example):
/** Send signed int */
void put_i16(const STREAM *p, const int16_t num);
Here's my UART files:
uart.h
extern STREAM* uart;
uart.c
// Shared stream instance
static STREAM _uart_singleton;
STREAM* uart;
void uart_init(uint16_t ubrr) {
// uart init code here
// Create the stream
_uart_singleton.tx = &uart_tx; // function pointers
_uart_singleton.rx = &uart_rx;
uart = &_uart_singleton; // expose a pointer to it
}
I'm not sure about this. It works, but is it the right way to do it? Should I just use Malloc instead?
Why I ask this, it's a library code and I want it to be as clean and "correct" as possible
The global pointer is unnecessary ( as are all globals ), and unsafe - it is non-const; any code with access to the pointer could modify _uart_singleton
.
uart.h
const STREAM* getUart() ;
...
uart.c
// Shared stream instance
static STREAM _uart_singleton = {0} ;
const STREAM* getUart()
{
// Return singleton if initialised,
// otherwise NULL
return _uart_singleton.rx != 0 &&
_uart_singleton.tx != 0 ? _uart_singleton :
NULL ;
}
void uart_init(uint16_t ubrr)
{
// uart init code here
// Create the stream
_uart_singleton.tx = &uart_tx; // function pointers
_uart_singleton.rx = &uart_rx;
}
So long as all the functions that access STREAM
members are defined withing uart.c, then you can also benefit from making STREAM an opaque type (Lundin's suggestion in comment) by using an incomplete struct declaration in the header thus:
uart.h
struct sStream ;
typedef struct sStream STREAM ;
const STREAM* getUart() ;
...
uart.c
// Shared stream instance
struct sStream
{
void (*tx) (uint8_t b);
uint8_t (*rx) (void);
} _uart_singleton = {0} ;
const STREAM* getUart()
{
// Return singleton if initialised,
// otherwise NULL
return _uart_singleton.rx != 0 &&
_uart_singleton.tx != 0 ? _uart_singleton :
NULL ;
}
...
This prevents any code outside of uart.c from calling the rx
and tx
functions directly or accessing any other members.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.