Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static library structs and includes

I'm building up a static embedded C-library, which has an API header file, that lists all available functions. I am not sure about a few thing, that I would like to clarify first before I start implementing it.

First of all, since this is a C-Library for embedded systems, which has hardware FPU, I'm not sure what I should include to calculate the math functions like sinf() etc. Normally I use the hardware specific includes, but that is not avialable in this static C-library, since it can run on some STM32 or even some AVR etc. How can I solve this problem?

Further Lets say I have the files foo.c and foo.h, which provide some hidden functions in the library and then there is the api.h, which can be seen by a user and api.c, which is also hidden. In foo.h are now some structs defined, that I would like to return in a callback to a user. Since this structs are hidden, I'm not sure how I should handle this callbacks. Should I implement a handler in api.c, which maps the structs from the callback from foo.c and pass them to the users callback, where the structs are redefined (with different name) in api.h or are there solutions with less overhead?

When I would define the necessary structs for foo.h in api.h, I would need to include api.h in foo.h, but also foo.h in api.h, that is not a good idea I think.

like image 410
HansPeterLoft Avatar asked Feb 27 '26 07:02

HansPeterLoft


1 Answers

For the first part of the question, the mathematical operations like sinf should be handled by the C standard library (you should check you specific version for support on your architecture). You can then use the math.h header and its functions, the compiler should then use the FPU to make the floating point computations.


For the second part, the usual way to show the user a hidden structure is with a foward declaration but the user will have to interact with the structure through pointers and access functions.

With you example, say we have four files:

  • api.h: public header
  • api.c: source code for functions from the public header
  • foo.h: library internal header (will not be shipped to the end-user)
  • foo.c: source code for the internal functions

The api.h is the only interesting file of those (have no changes).

// file: api.h
#ifndef API_H
#define API_H

struct foo; // forward declaration of the foo structure

typedef void (*callback_t)(struct foo*); // typedef for a callback taking a
                                         // struct foo argument

void set_callback(callback_t fn);
#endif

Now we have a type for the callback and the type of the structure given to the callback, but the user cannot interact with the structure itself, since the compiler only knows it exists but does not know its content (nor storage size).

When the user writes a callback like in the below code, the user will need to have some access functions.

#include "api.h"

void user_callback(struct foo* arg) {
    // user code here
}

The access functions are usually defined like this:

// in the file api.h or another header that the user has access to

int foo_get_value1(struct foo* arg);
void foo_set_value1(struct foo* arg, int new_value);

and those functions would be implemented in foo.c

struct foo {
    int value1;
    int value2;
};

int foo_get_value1(struct foo* arg) {
    return arg->value1;
}

void foo_set_value1(struct foo* arg, int new_value) {
    arg->value1 = new_value;
}

This approach has the added advantage that your foo_set functions can do validity checks to make sure you have appropriate values in your structure.

Please note: I did not add any checks to my access functions to avoid cluttering the code, but when passing a pointer to a function, you should always check it for NULL

like image 72
Simon Doppler Avatar answered Mar 01 '26 22:03

Simon Doppler