Forums

Static allocation and data hiding

Started by pozz March 3, 2021
It's a common problem in embedded projects. You want to hide private 
data of modules, but you don't want to use dynamic memory allocation.

In the .h file you write the typedef only and in .c file you write the 
full definition of the struct.
In this way you can't statically allocate the struct, because the 
compiler doesn't know the inner definition. If you want to use static 
allocation, you need to show the inner definition in the .h file, i.e. 
define it publicly.

FreeRTOS project uses another strategy. The real private definition is 
in the .c file, so hiding data. But the .h file has a dummy definition.

--- start of module.h ---
typedef struct myclass_s myclass;

typedef struct {
   uint8_t dummy1;
   uint16_y dummy2;
   char dummy3[24];
} static_myclass;

myclass *myclass_create_static(static_myclass *buf, uint8_t age, 
uint16_t sn, consr char *name);
--- end of module.h ---

--- start of module.c ---
struct myclass_s {
   uint8_t age;
   uint16_y serial_number;
   char name[24];
};

myclass *myclass_create_static(static_myclass *buf, uint8_t age, 
uint16_t sn, consr char *name) {
   myclass *obj = (myclass *)buf;
   obj->age = age;
   obj->serial_number = sn;
   memcpy(obj->name, name, sizeof(obj->name));
   return obj;
}
--- end of module.c ---


Do you have better strategies?
On 03/03/2021 13:16, pozz wrote:
> It's a common problem in embedded projects. You want to hide private > data of modules, but you don't want to use dynamic memory allocation. > > In the .h file you write the typedef only and in .c file you write the > full definition of the struct. > In this way you can't statically allocate the struct, because the > compiler doesn't know the inner definition. If you want to use static > allocation, you need to show the inner definition in the .h file, i.e. > define it publicly. > > FreeRTOS project uses another strategy. The real private definition is > in the .c file, so hiding data. But the .h file has a dummy definition. > > --- start of module.h --- > typedef struct myclass_s myclass; > > typedef struct { > � uint8_t dummy1; > � uint16_y dummy2; > � char dummy3[24]; > } static_myclass; > > myclass *myclass_create_static(static_myclass *buf, uint8_t age, > uint16_t sn, consr char *name); > --- end of module.h --- > > --- start of module.c --- > struct myclass_s { > � uint8_t age; > � uint16_y serial_number; > � char name[24]; > }; > > myclass *myclass_create_static(static_myclass *buf, uint8_t age, > uint16_t sn, consr char *name) { > � myclass *obj = (myclass *)buf; > � obj->age = age; > � obj->serial_number = sn; > � memcpy(obj->name, name, sizeof(obj->name)); > � return obj; > } > --- end of module.c --- > > > Do you have better strategies?
Neither C nor C++ provide good features for data hiding like this - FreeRTOS' method is perhaps the least bad way to get something that is valid according to the rules for compatible types while also avoiding the overhead of pointers and non-static data. But it has the disadvantage that the type needs to be defined twice, leading to maintenance inconvenience. With C++, you can do this: // myclass.h #include "myclass_imp.h" struct myclass : public myclass_imp { // Stuff you want to be documented and easily // seen by users of the class } // myclass_imp.h // Don't rely on the details in this file!! struct myclass_imp { // Hidden stuff } People can still see the details in the class if they want to, but at least it is a bit separate and out of the way. (I don't bother with a second header for the implementation class - I just put it in the second half of the main header.)
On 3/3/21 7:16 AM, pozz wrote:
> It's a common problem in embedded projects. You want to hide private > data of modules, but you don't want to use dynamic memory allocation. > > In the .h file you write the typedef only and in .c file you write the > full definition of the struct. > In this way you can't statically allocate the struct, because the > compiler doesn't know the inner definition. If you want to use static > allocation, you need to show the inner definition in the .h file, i.e. > define it publicly. > > FreeRTOS project uses another strategy. The real private definition is > in the .c file, so hiding data. But the .h file has a dummy definition. > ... > Do you have better strategies?
C provides at the language level data hiding only at a meta level. You can define in your documentation what information the user is allowed to assume is true, and what they shouldn't. You can perhaps use nameing conventions to make it more obvious when that principle is being violated (things like leading underscores in the name to mark things that shouldn't be assumed to be promised). This goes in line with the principle that C trusts the programmer, and is only trying to help fight off Murphy and not Machiavelli. The FreeRTOS method of dual structure definitions is one way to make that naming much more obvious (using names like dummy1, but comes at the cost of needing to maintain two concurrent versions of the structures, and there HAVE been cases where the two have gotten out of sync (I don't know if they have added a sizeof check to the C file to at least catch the problem when it occurs). It also comes at the cost that the program technically has undefined behavior due to a violation of the One Definition Rule (The type of the handle, and thus that type of that parameter to the functions, changes depending on whether you are in implementation code or user code). C++ improves this slightly, by allowing you to assign visibility (public/protected/private) to members, and will generate an error if someone accesses a member they aren't supposed to. Other languages can hide the information more completely, put at the cost that either you can't statically allocate the object (because you need to know its size to allocate it) or the compiler needs a back door that allows IT to peek at the full definition to get its size. The user always has the option to peek at the source module if it is available, or decode the compilers output to get information about the structure. Often, the method provided by C is good enough, and you can always say to the programmer that has their program break because the used information they weren't supposed to use that its a problem of their own making. (This presumes it isn't a security issue, but trusting in hiding to provide security starts on a losing basis).