Reputation: 309
In C, you can define structures to hold an assortment of variables;
typedef struct {
float sp;
float K; // interactive form - for display only
float Ti; // values are based in seconds
float Td;
} pid_data_t;
But lets say that K
, Ti
, and Td
should never be set publicly, and should only be used for storing the values after they have been manipulated. So, I want these values not to be updated by;
pid_data_t = pid_data;
pid_data.K = 10; // no good! changing K should be done via a function
I want them to be set via a function;
int8_t pid_set_pid_params(float new_K_dash, float new_Ti_dash,
float new_Td_dash)
{
… // perform lots of things
pid_data->K = new_K_dash;
pid_data->Ti = new_Ti_dash;
pid_data->Td = new_Td_dash;
}
Any thoughts on this? I know C++ uses like a get/set property, but was wondering what people might do on C.
Upvotes: 5
Views: 4963
Reputation: 162164
The canonical way to do this is by using a combination of opaque pointers and public structs, along with allocators, getters and setters for the private elements. About along these lines:
foo.h
typedef struct Foo {
/* public elements */
} Foo;
Foo *new_Foo(void);
void Foo_something_opaque(Foo* foo);
foo.c
#include "foo.h"
typedef struct Private_Foo_ {
struct Foo foo;
/* private elements */
} Private_Foo_;
Foo *new_Foo(void)
{
Private_Foo_ *foo = malloc(sizeof(Private_Foo_));
/* initialize private and public elements */
return (Foo*) foo;
}
void Foo_something_opaque(Foo *foo)
{
Private_Foo_ *priv_foo = (Private_Foo_*) foo;
/* do something */
}
This woks, because C guarantees, that the address of a struct variable always is equal to the address of the very first struct element. We can use this to have a Private_Foo_ struct, containing a public Foo at the beginning, giving out pointers to the whole thing, with the compilation units not having access to the Private_Foo_ struct defintion just seeing some memory without any context.
It should be noted that C++ works quite similar behind the curtains.
As KereekSB pointed out, this will break if used in a array.
I say: Then don't make Foo f[]
, however tempting, but make an arrays of pointers to Foo: Foo *f[]
.
If one really insists on using it in arrays do the following:
foo_private.h
typedef struct Private_Foo_ {
/* private elements */
} Private_Foo_;
static size_t Private_Foo_sizeof(void) { return sizeof(Private_Foo_); }
foo_private.h is written in a way, that it can be compiled into an object file. Use some helper program to link it and use the result of Private_Foo_sizeof()
to generate the actual, plattform dependent foo.h from some foo.h.in file.
foo.h
#include
#define FOO_SIZEOF_PRIVATE_ELEMENTS <generated by preconfigure step>
typedef struct Foo_ {
/* public elements */
char reserved[FOO_SIZEOF_PRIVATE_ELEMENTS];
} Foo;
Foo *new_Foo(void);
void Foo_something_opaque(Foo* foo);
foo.c
#include "foo.h"
#include "foo_private.h"
Foo *new_Foo(void)
{
Foo *foo = malloc(sizeof(Foo));
/* initialize private and public elements */
return (Foo*) foo;
}
void Foo_something_opaque(Foo *foo)
{
Private_Foo_ *priv_foo = (Private_Foo_*) foo.reserved;
/* do something */
}
IMHO this is really messy. Now I'm a fan of smart containers (unfortunately there's no standard container library for C). Anyway: In such a container is creates through a function like
Array *array_alloc(size_t sizeofElement, unsigned int elements);
void *array_at(Array *array, unsigned int index);
/* and all the other functions expected of arrays */
See the libowfaw for an example of such an implementation. Now for the type Foo it was trivial to provide a function
Array *Foo_array(unsigned int count);
Upvotes: 2
Reputation: 1202
Object orientation is a way of thinking and modelling, data encapsulation where struct data should not be modified directly by the user can be implemented this way:
my_library.h
#ifndef __MY_LIBRARY__
#define __MY_LIBRARY__
typedef void MiObject;
MyObject* newMyObject();
void destroyMyObject(MyObject*)
int setMyObjectProperty1(MyObject*,someDataType1*);
/*Return a pointer to the data/object, classic pass by value */
someDataType1* getMyObjectProperty2Style1(MyObject*);
int setMyObjectProperty2(MyObject*,someDataType2*);
/* The data/object is passed through reference */
int getMyObjectProperty2Style2(MyObject*,someDataType2**);
/* Some more functions here */
#endif
my_library.c
struct _MyHiddenDataType{
int a;
char* b;
..
..
};
MyObject* newMyObject(){
struct _MyHiddenData* newData = (struct _MyHiddenData*)malloc(sizeof(struct _MyHiddenData);
//check null, etc
//initialize data, etc
return (MyObject*)newData;
}
int setMyObjectProperty1(MyObject* object,someDataType1* somedata){
struct _MyHiddenData* data = (struct _MyHiddenData*)object;
//check for nulls, and process somedata
data->somePropery=somedata;
}
someDataType1* getMyObjectProperty2Style1(MyObject*){
struct _MyHiddenData* data = (struct _MyHiddenData*)object;
//check for nulls, and process somedata
return data->someProperty;
}
/* Similar code for the rest */
And this way you have encapsulated the struct properties as if they were private. On the same manner static functions inside my_libray.c would behave as private functions. Get a good look at C and you'll see, that your imagination is the limit to what you can do.
Upvotes: 0
Reputation: 476930
Your public interface should only offer an opaque pointer (maybe DATA*
, or data_handle
), as well as handler functions create_data()
, set_data_value()
, read_data_value()
, free_data()
, etc., which operate on the opaque pointer.
Much like FILE*
.
Just don't give your clients the internal header files :-)
// library.h
typedef struct data_t * data_handle;
data_handle create_data();
void free_data(data_handle);
Private implementation (don't ship):
#include "library.h"
struct data_t
{
/* ... */
};
data_handle create_data() { return malloc(sizeof(struct data_t)); }
void free_data(data_handle h) { free(h); }
/* etc. etc. */
Upvotes: 5
Reputation: 44288
in C, by convention....
for OO C like this...
I'd have a pid_data_create(&data) // initializes your struct
and pid_data_set_proportional_gain(&data, 0.1);
etc...
so basically achieving a C++ ish class... prefix all functions with the "class" / "struct" name and always pass the struct * as the first parameter.
also, it should store function pointers for polymorphisim, and you shouldn't call those function pointers directly, again, have a function that takes your struct as a parameter, and then the can make the function pointer call (can check for nulls, fake inheritance/virtual functions, and other stuff)
Upvotes: 2