How to avoid a double copy when implementing get API methods - c

I Want to optimize a thread-safe "get" function for a variable globalBig that looks like the following
extern struct big *globalBig; // points to a big struct defined elsewhere
struct big getGlobalBig(void) {
// lock globalBig here...
struct big ret = *globalBig; // <------ copy #1 (into stack of getGlobalBig)
// unlock globalBig here...
return ret; // <------ copy #2 (into stack of caller)
}
When I look at the assembly, I see that there are two invocations of memcpy, one into the stack of the get method, and the second into the stack of the calling function caused by the return. I DO NOT want to have to pass in a pointer argument into getGlobalBig() because I want a function that returns an rvalue so that I could do stuff like:
if(getGlobalBig().someField == 9) {
// yay
}
I am also aware that I could do a single copy by skipping the unlocking of the variable, leaving that to the caller as a cleanup activity, but this is undesirable as I want to spend as little time as possible with the lock on.
struct big getGlobalBig(void) {
// lock globalBig here...
return *globalBig; // <------ copy #1 (directly into stack of caller, caller is responsible for unlocking)
}
So based on those needs, is there a way to avoid the two copies? For example, I envision something like "returning in advance":
struct big getGlobalBig(void) {
// lock globalBig here...
// single copy directly into the destination struct allocated in the stack area of the caller
*(struct big*)SOMEHOW_GET_THE_ADDR_THAT_THIS_FUNCTION_COPIES_TO_WHEN_RETURNING = *globalBig;
// unlock globalBig here...
return; // return nothing, you already "did" (?)
}

Related

Understanding a stack-use-after-scope error

I am working on a multithreaded client using C and the pthreads library, using a boss/worker arch design and am having issues understanding/debugging a stack-use-after-scope error that is causing my client to fail. (I am kinda new to C)
I have tried multiple things, including defining the variable globally, passing a double pointer reference, etc.
Boss logic within main:
for (i = 0; i < nrequests; i++)
{
struct Request_work_item *request_ctx = malloc(sizeof(*request_ctx));
request_ctx->server = server;
request_ctx->port = port;
request_ctx->nrequests = nrequests;
req_path = get_path(); //Gets a file path to work on
request_ctx->path = req_path;
steque_item work_item = &request_ctx; // steque_item is a void* so passing it a pointer to the Request_work_item
pthread_mutex_lock(&mutex);
while (steque_isempty(&work_queue) == 0) //Wait for the queue to be empty to add more work
{
pthread_cond_wait(&c_boss, &mutex);
}
steque_enqueue(&work_queue, work_item); //Queue the workItem in a workQueue (type steque_t, can hold any number of steque_items)
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&c_worker);
}
Worker logic inside a defined function:
struct Request_work_item **wi;
while (1)
{
pthread_mutex_lock(&mutex);
while (steque_isempty(&work_queue) == 1) //Wait for work to be added to the queue
{
pthread_cond_wait(&c_worker, &mutex);
}
wi = steque_pop(&work_queue); //Pull the steque_item into a Request_work_item type
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&c_boss);
char *path_to_file = (*wi)->path; //When executing, I get this error in this line: SUMMARY: AddressSanitizer: stack-use-after-scope
...
...
...
continues with additional worker logic
I expect the worker to pull the work_item from the queue, dereference the values and then perform some work. However, I keep getting AddressSanitizer: stack-use-after-scope, and the information for this error online is not very abundant so any pointers would be greatly appreciated.
The red flag here is that &request_ctx is the address of a local variable. It's not the pointer to the storage allocated with malloc, but the address of the variable which holds that storage. That variable is gone once this scope terminates, even though the malloc-ed block endures.
Maybe the fix is simply to delete the address-of & operator in this line?
steque_item work_item = &request_ctx; // steque_item is a void* so passing
// it a pointer to the Request_work_item
If we do that, then the comment actually tells the truth. Because otherwise we're making work_item a pointer to a pointer to the Request_work_item.
Since work_item has type void*, it compiles either way, unfortunately.
If the consumer of the item on the other end of the queue is extracting it as a Request_work_item *, then you not only have an access to an object that has gone out of scope, but also a type mismatch even if that object happens to still be in the producer's scope when the consumer uses it. The consumer ends up using a piece of the producer's stack as if it were a Request_work_item structure. Edit: I see that you are using a pointer-to-pointer when dequeuing the item and accessing it as (*wi)->path. Think about changing the design to avoid doing that. Or else, that wi pointer has to be dynamically allocated also, and freed. The producer has to do something like:
struct Request_work_item **p_request_ctx = malloc(sizeof *p_request_ctx);
struct Request_work_item *request_ctx = malloc(sizeof *request_ctx);
if (p_request_ctx && request_ctx) {
*p_request_ctx = request_ctx;
request_ctx->field = init_value;
// ... etc
// then p_request_ctx is enqueued.
The consumer then has to free the structure, and also free the pointer. That extra pointer just seems like pure overhead here; it doesn't provide any essential or useful level of indirection.

Efficient way to detect changes in structure members?

This seems like it should be simple but I wasn't able to find much related to it. I have structure which has different fields used to store data about the program operation. I want to log that data so that I can analyse it later. Attempting to continuously log data over the course of the programs operation eats up a lot of resources. Thus I would only like to call the logging function when the data has changed. I would love it if there was an efficient way to check whether the structure members have updated. Currently I am playing a shell game with 3 structures (old, current, and new) in order to detect when the data has changed. Thanks in advance.
You may track structures and its hashes in your log function.
Let you have a hash function:
int hash(void* ptr, size_t size);
Let you have a mapping from pointer to struct to struct's hash like:
/* Stores hash value for ptr*/
void ptr2hash_update_hash(void* ptr, int hash);
/* Remove ptr from mapping */
void ptr2hash_remove(void* ptr);
/* Returns 0 if ptr was not stored, or stored has otherwise*/
int ptr2hash_get_hash(void* ptr);
Then you may check if your object was changed between log calls like this:
int new_hash = hash(ptr, sizeof(TheStruct));
int old_hash = ptr2hash_get_hash(ptr);
if (old_hash == new_hash)
return;
ptr2hash_update_hash(ptr, new_hash);
/* Then do the logging */
Don't forget to remove ptr from mapping when you do free(ptr) :)
Here is simple hash table implementation, you will need it to implement ptr2hash mapping.
Simple hash functions are here.
If you're running on Linux (x86 or x86_64) then another possible approach is the following:
Install a segment descriptor for a non-writable segment in the local descriptor table using the modify_ldt system call. Place your data inside this segment (or install the segment such that your data structure is within it).
Upon write access, your process will receive a SIGSEGV (segmentation fault). Install a handler using sigaction to catch segmentation faults. Within that handler, first check that the fault occurred inside the previously set segment (si_addr member of the siginfo_t) and if so prepare to record a notification. Now, change the segment descriptor such that the segment becomes writable and return from the signal handler.
The write will now be performed, but you need a way to change the segment to be non-writable again and to actually check what was written and if your data actually changed.
A possible approach could be to send oneself (or a "delay" process and then back to the main process) another signal (SIGUSR1 for example), and doing the above in the handler for this signal.
Is this portable? No.
Is this relyable? No.
Is this easy to implement? No.
So if you can, and I really hope you do, use a interface like already suggested.
The easiest way what you can try is, You can just keep two structure pointers. Once you are receiving the new updated values that time you can just compare the new structure pointer with the old structure pointer, and if any difference is there you can detect it and then you can update to old structure pointer so that you can detect further changes in updated value in future.
typedef struct testStruct
{
int x;
float y;
}TESTSTRUCT;
TESTSTRUCT* getUpdatedValue()
{
TESTSTRUCT *ptr;
ptr->x = 5;
ptr->y = 6;
//You can put your code to update the value.
return ptr;
}
void updateTheChange(TESTSTRUCT* oldObj,TESTSTRUCT* newObj)
{
cout << "Change Detected\n";
oldObj = newObj;
}
int main()
{
TESTSTRUCT *oldObj = NULL;
TESTSTRUCT *newObj = NULL;
newObj = getUpdatedValue();
//each time a value is updated compae with the old structure
if(newObj == oldObj)
{
cout << "Same" << endl;
}
else
{
updateTheChange(oldObj,newObj);
}
return 0;
}
I am not sure, it gives you your exact answer or not.
Hope this Helps.

C - cast void* to structure in thrd_create()

I know there's a lot of answered questions about casting void* to struct but I can't manage to get it work the right way.
Well I want to create a thread which will play music in background. I have a structure which gather the loaded music file array and the start and end index :
typedef unsigned char SoundID;
typedef unsigned char SongID;
typedef struct {
Mix_Music *songs[9]; // array of songs
SongID startId; // index of first song to play
SongID endId; // index of last song to play
} SongThreadItem;
Then I want to play the songs by creating a thread and passing the function which actually plays the songs to the thread_create() function.
int play_songs(Mix_Music *songs[9], SongID startId, SongID endId, char loop){
thrd_t thrd;
SongThreadItem _item;
SongThreadItem *item = &_item;
memcpy(item->songs, songs, sizeof(item->songs));
item->startId = startId;
item->endId = endId;
printf("item->startId is %i\n", item->startId);
printf("item->endId is %i\n", item->endId);
thrd_create_EC(thrd_create(&thrd, audio_thread_run, item));
return 0;
}
int audio_thread_run(void *arg){
SongThreadItem *item = arg; // also tried with = (SongThreadItem *)arg
printf("item->startId is %i\n", item->startId);
printf("item->endId is %i\n", item->endId);
free(item);
return 0;
}
Then I get the following output:
item->startId is 0
item->endId is 8
item->startId is 6
item->endId is 163
The value retrieved inside audio_thread_run() aren't the one expected. I don't know if I put enough code to let someone find my error, I try to make it minimal because it's part of a bigger project.
Thanks in advance for your help.
SongThreadItem _item;
SongThreadItem *item = &_item; // bug
That's a problem there: you're giving the thread a pointer to a stack variable. The stack will get overwritten by pretty much anything going on in the main thread. You need to allocate dynamic memory here (with malloc), and take care of freeing it when no-longer needed (perhaps in the thread routine itself).
Other options would be a global structure that keeps track of all the active threads and their starting data, or something like that. But it will involve dynamic allocations unless the count of threads is fixed at compile time.
The thread runs asynchronously but you are passing it a pointer to SongThreadItem that is on the stack of the thread that calls play_songs().
If you have only a single thread calling play_songs() and this is not called again until you are done with the item, you can make the definition _item like this:
static SongThreadItem _item;
so that it is in the data segment and will not be overwritten.
If you don't know when and who will call play_songs() then just malloc the _item and free it in the thread when you are done:
...
SongThreadItem *item = (SongThreadItem *)malloc(sizeof(SongThreadItem));
...
The latter is usually the better idea. Think of it as passing the ownership of the data to the new thread. Of course production quality code should free the item if the thread creation fails.

Store extra data in a c function pointer

Suppose there is a library function (can not modify) that accept a callback (function pointer) as its argument which will be called at some point in the future. My question: is there a way to store extra data along with the function pointer, so that when the callback is called, the extra data can be retrieved. The program is in c.
For example:
// callback's type, no argument
typedef void (*callback_t)();
// the library function
void regist_callback(callback_t cb);
// store data with the function pointer
callback_t store_data(callback_t cb, int data);
// retrieve data within the callback
int retrieve_data();
void my_callback() {
int a;
a = retrieve_data();
// do something with a ...
}
int my_func(...) {
// some variables that i want to pass to my_callback
int a;
// ... regist_callback may be called multiple times
regist_callback(store_data(my_callback, a));
// ...
}
The problem is because callback_t accept no argument. My idea is to generate a small piece of asm code each time to fill into regist_callback, when it is called, it can find the real callback and its data and store it on the stack (or some unused register), then jump to the real callback, and inside the callback, the data can be found.
pseudocode:
typedef struct {
// some asm code knows the following is the real callback
char trampoline_code[X];
callback_t real_callback;
int data;
} func_ptr_t;
callback_t store_data(callback_t cb, int data) {
// ... malloc a func_ptr_t
func_ptr_t * fpt = malloc(...);
// fill the trampoline_code, different machine and
// different calling conversion are different
// ...
fpt->real_callback = cb;
fpt->data = data;
return (callback_t)fpt;
}
int retrieve_data() {
// ... some asm code to retrive data on stack (or some register)
// and return
}
Is it reasonable? Is there any previous work done for such problem?
Unfortunately you're likely to be prohibited from executing your trampoline in more and more systems as time goes on, as executing data is a pretty common way of exploiting security vulnerabilities.
I'd start by reporting the bug to the author of the library. Everybody should know better than to offer a callback interface with no private data parameter.
Having such a limitation would make me think twice about how whether or not the library is reentrant. I would suggest ensuring you can only have one call outstanding at a time, and store the callback parameter in a global variable.
If you believe that the library is fit for use, then you could extend this by writing n different callback trampolines, each referring to their own global data, and wrap that up in some management API.

Opaque C structs: various ways to declare them

I've seen both of the following two styles of declaring opaque types in C APIs. What are the various ways to declare opaque structs/pointers in C? Is there any clear advantage to using one style over the other?
Option 1
// foo.h
typedef struct foo * fooRef;
void doStuff(fooRef f);
// foo.c
struct foo {
int x;
int y;
};
Option 2
// foo.h
typedef struct _foo foo;
void doStuff(foo *f);
// foo.c
struct _foo {
int x;
int y;
};
My vote is for the third option that mouviciel posted then deleted:
I have seen a third way:
// foo.h
struct foo;
void doStuff(struct foo *f);
// foo.c
struct foo {
int x;
int y;
};
If you really can't stand typing the struct keyword, typedef struct foo foo; (note: get rid of the useless and problematic underscore) is acceptable. But whatever you do, never use typedef to define names for pointer types. It hides the extremely important piece of information that variables of this type reference an object which could be modified whenever you pass them to functions, and it makes dealing with differently-qualified (for instance, const-qualified) versions of the pointer a major pain.
Option 1.5 ("Object-based" C Architecture):
I am accustomed to using Option 1, except where you name your reference with _h to signify it is a "handle" to a C-style "object" of this given C "class". Then, you ensure your function prototypes use const wherever the content of this object "handle" is an input only, and cannot be changed, and don't use const wherever the content can be changed. So, do this style:
// -------------
// my_module.h
// -------------
// An opaque pointer (handle) to a C-style "object" of "class" type
// "my_module" (struct my_module_s *, or my_module_h):
typedef struct my_module_s *my_module_h;
void doStuff1(my_module_h my_module);
void doStuff2(const my_module_h my_module);
// -------------
// my_module.c
// -------------
// Definition of the opaque struct "object" of C-style "class" "my_module".
struct my_module_s
{
int int1;
int int2;
float f1;
// etc. etc--add more "private" member variables as you see fit
};
Here's a full example using opaque pointers in C to create objects. The following architecture might be called "object-based C":
//==============================================================================================
// my_module.h
//==============================================================================================
// An opaque pointer (handle) to a C-style "object" of "class" type "my_module" (struct
// my_module_s *, or my_module_h):
typedef struct my_module_s *my_module_h;
// Create a new "object" of "class" "my_module": A function that takes a *pointer to* an
// "object" handle, `malloc`s memory for a new copy of the opaque `struct my_module_s`, then
// points the user's input handle (via its passed-in pointer) to this newly-created "object" of
// "class" "my_module".
void my_module_open(my_module_h * my_module_h_p);
// A function that takes this "object" (via its handle) as an input only and cannot modify it
void my_module_do_stuff1(const my_module_h my_module);
// A function that can modify the private content of this "object" (via its handle) (but still
// cannot modify the handle itself)
void my_module_do_stuff2(my_module_h my_module);
// Destroy the passed-in "object" of "class" type "my_module": A function that can close this
// object by stopping all operations, as required, and `free`ing its memory.
void my_module_close(my_module_h my_module);
//==============================================================================================
// my_module.c
//==============================================================================================
// Definition of the opaque struct "object" of C-style "class" "my_module".
// - NB: Since this is an opaque struct (declared in the header but not defined until the source
// file), it has the following 2 important properties:
// 1) It permits data hiding, wherein you end up with the equivalent of a C++ "class" with only
// *private* member variables.
// 2) Objects of this "class" can only be dynamically allocated. No static allocation is
// possible since any module including the header file does not know the contents of *nor the
// size of* (this is the critical part) this "class" (ie: C struct).
struct my_module_s
{
int my_private_int1;
int my_private_int2;
float my_private_float;
// etc. etc--add more "private" member variables as you see fit
};
void my_module_open(my_module_h * my_module_h_p)
{
// Ensure the passed-in pointer is not NULL (since it is a core dump/segmentation fault to
// try to dereference a NULL pointer)
if (!my_module_h_p)
{
// Print some error or store some error code here, and return it at the end of the
// function instead of returning void.
goto done;
}
// Now allocate the actual memory for a new my_module C object from the heap, thereby
// dynamically creating this C-style "object".
my_module_h my_module; // Create a local object handle (pointer to a struct)
// Dynamically allocate memory for the full contents of the struct "object"
my_module = malloc(sizeof(*my_module));
if (!my_module)
{
// Malloc failed due to out-of-memory. Print some error or store some error code here,
// and return it at the end of the function instead of returning void.
goto done;
}
// Initialize all memory to zero (OR just use `calloc()` instead of `malloc()` above!)
memset(my_module, 0, sizeof(*my_module));
// Now pass out this object to the user, and exit.
*my_module_h_p = my_module;
done:
}
void my_module_do_stuff1(const my_module_h my_module)
{
// Ensure my_module is not a NULL pointer.
if (!my_module)
{
goto done;
}
// Do stuff where you use my_module private "member" variables.
// Ex: use `my_module->my_private_int1` here, or `my_module->my_private_float`, etc.
done:
}
void my_module_do_stuff2(my_module_h my_module)
{
// Ensure my_module is not a NULL pointer.
if (!my_module)
{
goto done;
}
// Do stuff where you use AND UPDATE my_module private "member" variables.
// Ex:
my_module->my_private_int1 = 7;
my_module->my_private_float = 3.14159;
// Etc.
done:
}
void my_module_close(my_module_h my_module)
{
// Ensure my_module is not a NULL pointer.
if (!my_module)
{
goto done;
}
free(my_module);
done:
}
Simplified example usage:
#include "my_module.h"
#include <stdbool.h>
#include <stdio.h>
int main()
{
printf("Hello World\n");
bool exit_now = false;
// setup/initialization
my_module_h my_module = NULL;
// For safety-critical and real-time embedded systems, it is **critical** that you ONLY call
// the `_open()` functions during **initialization**, but NOT during normal run-time,
// so that once the system is initialized and up-and-running, you can safely know that
// no more dynamic-memory allocation, which is non-deterministic and can lead to crashes,
// will occur.
my_module_open(&my_module);
// Ensure initialization was successful and `my_module` is no longer NULL.
if (!my_module)
{
// await connection of debugger, or automatic system power reset by watchdog
log_errors_and_enter_infinite_loop();
}
// run the program in this infinite main loop
while (exit_now == false)
{
my_module_do_stuff1(my_module);
my_module_do_stuff2(my_module);
}
// program clean-up; will only be reached in this case in the event of a major system
// problem, which triggers the infinite main loop above to `break` or exit via the
// `exit_now` variable
my_module_close(my_module);
// for microcontrollers or other low-level embedded systems, we can never return,
// so enter infinite loop instead
while (true) {}; // await reset by watchdog
return 0;
}
The only improvements beyond this would be to:
Implement full error handling and return the error instead of void. Ex:
/// #brief my_module error codes
typedef enum my_module_error_e
{
/// No error
MY_MODULE_ERROR_OK = 0,
/// Invalid Arguments (ex: NULL pointer passed in where a valid pointer is required)
MY_MODULE_ERROR_INVARG,
/// Out of memory
MY_MODULE_ERROR_NOMEM,
/// etc. etc.
MY_MODULE_ERROR_PROBLEM1,
} my_module_error_t;
Now, instead of returning a void type in all of the functions above and below, return a my_module_error_t error type instead!
Add a configuration struct called my_module_config_t to the .h file, and pass it in to the open function to update internal variables when you create a new object. This helps encapsulate all configuration variables in a single struct for cleanliness when calling _open().
Example:
//--------------------
// my_module.h
//--------------------
// my_module configuration struct
typedef struct my_module_config_s
{
int my_config_param_int;
float my_config_param_float;
} my_module_config_t;
my_module_error_t my_module_open(my_module_h * my_module_h_p,
const my_module_config_t *config);
//--------------------
// my_module.c
//--------------------
my_module_error_t my_module_open(my_module_h * my_module_h_p,
const my_module_config_t *config)
{
my_module_error_t err = MY_MODULE_ERROR_OK;
// Ensure the passed-in pointer is not NULL (since it is a core dump/segmentation fault
// to try to dereference a NULL pointer)
if (!my_module_h_p)
{
// Print some error or store some error code here, and return it at the end of the
// function instead of returning void. Ex:
err = MY_MODULE_ERROR_INVARG;
goto done;
}
// Now allocate the actual memory for a new my_module C object from the heap, thereby
// dynamically creating this C-style "object".
my_module_h my_module; // Create a local object handle (pointer to a struct)
// Dynamically allocate memory for the full contents of the struct "object"
my_module = malloc(sizeof(*my_module));
if (!my_module)
{
// Malloc failed due to out-of-memory. Print some error or store some error code
// here, and return it at the end of the function instead of returning void. Ex:
err = MY_MODULE_ERROR_NOMEM;
goto done;
}
// Initialize all memory to zero (OR just use `calloc()` instead of `malloc()` above!)
memset(my_module, 0, sizeof(*my_module));
// Now initialize the object with values per the config struct passed in. Set these
// private variables inside `my_module` to whatever they need to be. You get the idea...
my_module->my_private_int1 = config->my_config_param_int;
my_module->my_private_int2 = config->my_config_param_int*3/2;
my_module->my_private_float = config->my_config_param_float;
// etc etc
// Now pass out this object handle to the user, and exit.
*my_module_h_p = my_module;
done:
return err;
}
And usage:
my_module_error_t err = MY_MODULE_ERROR_OK;
my_module_h my_module = NULL;
my_module_config_t my_module_config =
{
.my_config_param_int = 7,
.my_config_param_float = 13.1278,
};
err = my_module_open(&my_module, &my_module_config);
if (err != MY_MODULE_ERROR_OK)
{
switch (err)
{
case MY_MODULE_ERROR_INVARG:
printf("MY_MODULE_ERROR_INVARG\n");
break;
case MY_MODULE_ERROR_NOMEM:
printf("MY_MODULE_ERROR_NOMEM\n");
break;
case MY_MODULE_ERROR_PROBLEM1:
printf("MY_MODULE_ERROR_PROBLEM1\n");
break;
case MY_MODULE_ERROR_OK:
// not reachable, but included so that when you compile with
// `-Wall -Wextra -Werror`, the compiler will fail to build if you forget to handle
// any of the error codes in this switch statement.
break;
}
// Do whatever else you need to in the event of an error, here. Ex:
// await connection of debugger, or automatic system power reset by watchdog
while (true) {};
}
// ...continue other module initialization, and enter main loop
See also:
[another answer of mine which references my answer above] Architectural considerations and approaches to opaque structs and data hiding in C
Additional reading on object-based C architecture:
Providing helper functions when rolling out own structures
Additional reading and justification for valid usage of goto in error handling for professional code:
An argument in favor of the use of goto in C for error handling: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/Research_General/goto_for_error_handling_in_C/readme.md
*****EXCELLENT ARTICLE showing the virtues of using goto in error handling in C: "Using goto for error handling in C" - https://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c
Valid use of goto for error management in C?
Error handling in C code
Search terms to make more googlable: opaque pointer in C, opaque struct in C, typedef enum in C, error handling in C, c architecture, object-based c architecture, dynamic memory allocation at initialization architecture in c
bar(const fooRef) declares an immutable address as argument. bar(const foo *) declares an address of an immutable foo as argument.
For this reason, I tend to prefer option 2. I.e., the presented interface type is one where cv-ness can be specified at each level of indirection. Of course one can sidestep the option 1 library writer and just use foo, opening yourself to all sorts of horror when the library writer changes the implementation. (I.e., the option 1 library writer only perceives that fooRef is part of the invariant interface and that foo can come, go, be altered, whatever. The option 2 library writer perceives that foo is part of the invariant interface.)
I'm more surprised that no one's suggested combined typedef/struct constructions.
typedef struct { ... } foo;
Option 3: Give people choice
/* foo.h */
typedef struct PersonInstance PersonInstance;
typedef struct PersonInstance * PersonHandle;
typedef const struct PersonInstance * ConstPersonHandle;
void saveStuff (PersonHandle person);
int readStuff (ConstPersonHandle person);
...
/* foo.c */
struct PersonInstance {
int a;
int b;
...
};
...

Resources