Related
I have been writing C for a decent amount of time, and obviously am aware that C does not have any support for explicit private and public fields within structs. However, I (believe) I have found a relatively clean method of implementing this without the use of any macros or voodoo, and I am looking to gain more insight into possible issues I may have overlooked.
The folder structure isn't all that important here but I'll list it anyway because it gives clarity as to the import names (and is also what CLion generates for me).
- example-project
- cmake-build-debug
- example-lib-name
- include
- example-lib-name
- example-header-file.h
- src
- example-lib-name
- example-source-file.c
- CMakeLists.txt
- CMakeLists.txt
- main.c
Let's say that example-header-file.h contains:
typedef struct ExampleStruct {
int data;
} ExampleStruct;
ExampleStruct* new_example_struct(int, double);
which just contains a definition for a struct and a function that returns a pointer to an ExampleStruct.
Obviously, now if I import ExampleStruct into another file, such as main.c, I will be able to create and return a pointer to an ExampleStruct by calling
ExampleStruct* new_struct = new_example_struct(<int>, <double>);,
and will be able to access the data property like: new_struct->data.
However, what if I also want private properties in this struct. For example, if I am creating a data structure, I don't want it to be easy to modify the internals of it. I.e. if I've implemented a vector struct with a length property that describes the current number of elements in the vector, I wouldn't want for people to just be able to change that value easily.
So, back to our example struct, let's assume we also want a double field in the struct, that describes some part of internal state that we want to make 'private'.
In our implementation file (example-source-file.c), let's say we have the following code:
#include <stdlib.h>
#include <stdbool.h>
typedef struct ExampleStruct {
int data;
double val;
} ExampleStruct;
ExampleStruct* new_example_struct(int data, double val) {
ExampleStruct* new_example_struct = malloc(sizeof(ExampleStruct));
example_struct->data=data;
example_struct->val=val;
return new_example_struct;
}
double get_val(ExampleStruct* e) {
return e->val;
}
This file simply implements that constructor method for getting a new pointer to an ExampleStruct that was defined in the header file. However, this file also defines its own version of ExampleStruct, that has a new member field not present in the header file's definition: double val, as well as a getter which gets that value. Now, if I import the same header file into main.c, which contains:
#include <stdio.h>
#include "example-lib-name/example-header-file.h"
int main() {
printf("Hello, World!\n");
ExampleStruct* test = new_example(6, 7.2);
printf("%d\n", test->data); // <-- THIS WORKS
double x = get_val(test); // <-- THIS AND THE LINE BELOW ALSO WORK
printf("%f\n", x); //
// printf("%f\n", test->val); <-- WOULD THROW ERROR `val not present on struct!`
return 0;
}
I tested this a couple times with some different fields and have come to the conclusion that modifying this 'private' field, val, or even accessing it without the getter, would be very difficult without using pointer arithmetic dark magic, and that is the whole point.
Some things I see that may be cause for concern:
This may make code less readable in the eyes of some, but my IDE has arrow buttons that take me to and from the definition and the implementation, and even without that, a one line comment would provide more than enough documentation to point someone in the direction of where the file is.
Questions I'd like answers on:
Are there significant performance penalties I may suffer as a result of writing code this way?
Am I overlooking something that may make this whole ordeal pointless, i.e. is there a simpler way to do this or is this explicitly discouraged, and if so, what are the objective reasons behind it.
Aside: I am not trying to make C into C++, and generally favor the way C does things, but sometimes I really want some encapsulation of data.
Am I overlooking something that may make this whole ordeal pointless, i.e. is there a simpler way to do this or is this explicitly discouraged, and if so, what are the objective reasons behind it.
Yes: your approach produces undefined behavior.
C requires that
All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
(C17 6.2.7/2)
and that
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
[...]
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a
subaggregate or contained union), or
a character type.
(C17 6.5/7, a.k.a. the "Strict Aliasing Rule")
Your two definitions of struct ExampleStruct define incompatible types because they specify different numbers of members (see C17 6.2.7/1 for more details on structure type compatibility). You will definitely have problems if you pass instances by value between functions relying on different of these incompatible definitions. You will have trouble if you construct arrays of them, whether dynamically, automatically, or statically, and attempt to use those across boundaries between TUs using one definition and those using another. You may have problems even if you do none of the above, because the compiler may behave unexpectedly, especially when optimizing. DO NOT DO THIS.
Other alternatives:
Opaque pointers. This means you do not provide any definition of struct ExampleStruct in those TUs where you want to hide any of its members. That does not prevent declaring and using pointers to such a structure, but it does prevent accessing any members, declaring new instances, or passing or receiving instances by value. Where member access is needed from TUs that do not have the structure definition, it would need to be mediated by accessor functions.
Just don't access the "private" members. Do not document them in the public documentation, and if you like, explicity mark them (in code comments, for example) as reserved. This approach will be familiar to many C programmers, as it is used a lot for structures declared in POSIX system headers.
As long as the public has a complete definition for ExampleStruct, it can make code like:
ExampleStruct a = *new_example_struct(42, 1.234);
Then the below will certainly fail.
printf("%g\n", get_val(&a));
I recommend instead to create an opaque pointer and provide access public functions to the info in .data and .val.
Think of how we use FILE. FILE *f = fopen(...) and then fread(..., f), fseek(f, ...), ftell(f) and eventually fclose(f). I suggest this model instead. (Even if in some implementations FILE* is not opaque.)
Are there significant performance penalties I may suffer as a result of writing code this way?
Probably:
Heap allocation is expensive, and - today - usually not optimized away even when that is theoretically possible.
Dereferencing a pointer for member access is expensive; although this might get optimized away with link-time-optimization... if you're lucky.
i.e. is there a simpler way to do this
Well, you could use a slack array of the same size as your private fields, and then you wouldn't need to go through pointers all the time:
#define EXAMPLE_STRUCT_PRIVATE_DATA_SIZE sizeof(double)
typedef struct ExampleStruct {
int data;
_Alignas(max_align_t) private_data[EXAMPLE_STRUCT_PRIVATE_DATA_SIZE];
} ExampleStruct;
This is basically a type-erasure of the private data without hiding the fact that it exists. Now, it's true that someone can overwrite the contents of this array, but it's kind of useless to do it intentionally when you "don't know" what the data means. Also, the private data in the "real" definition will need to have the same, maximal, _AlignAs() as well (if you want the private data not to need to use AlignAs(), you will need to use the real alignment quantum for the type-erased version).
The above is C11. You can sort of do about the same thing by typedef'ing max_align_t yourself, then using an array of max_align_t elements for private data, with an appropriate length to cover the actual size of the private data.
An example of the use of such an approach can be found in CUDA's driver API:
Parameters for copying a 3D array: CUDA_MEMCPY3D vs
Parameters for copying a 3D array between two GPU devices: CUDA_MEMCPY3D_peer
The first structure has a pair of reserved void* fields, hiding the fact that it's really the second structure. They could have used an unsigned char array, but it so happens that the private fields are pointer-sized, and void* is also kind of opaque.
This causes undefined behaviour, as detailed in the other answers. The usual way around this is to make a nested struct.
In example.h, one defines the public-facing elements. struct example is not meant to be instantiated; in a sense, it is abstract. Only pointers that are obtained from one of it's (in this case, the) constructor are valid.
struct example { int data; };
struct example *new_example(int, double);
double example_val(struct example *e);
and in example.c, instead of re-defining struct example, one has a nested struct private_example. (Such that they are related by composite aggregation.)
#include <stdlib.h>
#include "example.h"
struct private_example {
struct example public;
double val;
};
struct example *new_example(int data, double val) {
struct private_example *const example = malloc(sizeof *example);
if(!example) return 0;
example->public.data = data;
example->val = val;
return &example->public;
}
/** This is a poor version of `container_of`. */
static struct private_example *example_upcast(struct example *example) {
return (struct private_example *)(void *)
((char *)example - offsetof(struct private_example, public));
}
double example_val(struct example *e) {
return example_upcast(e)->val;
}
Then one can use the object as in main.c. This is used frequently in linux kernel code for container abstraction. Note that offsetof(struct private_example, public) is zero, ergo example_upcast does nothing and a cast is sufficient: ((struct private_example *)e)->val. If one builds structures in a way that always allows casting, one is limited by single inheritance.
#include<stdio.h>
struct str
{
static int a ;
int b ;
} s ;
int main()
{
static int p , k ;
printf("%d %d",sizeof(p),sizeof(s));
getchar();
return 0;
}
above code is giving errors . But if I redefine the first member of the structure to 'int' rather than 'static int' then it runs fine . Why static members are not allowed in the structure and what is its significance ?
There's simply no such feature in C language. And there's no meaningful conceptual framework for such feature in C.
You see, in C++ terms, there's only one relevant difference between a static member variable and an ordinary global variable: the scope in which its name is declared and the corresponding naming syntax. A global variable could be called a, while a static member of the class would be called SomeClass::a. Besides scoped naming, there are no other differences. (I deliberately ignore other C++-specific features, like access control, since they don't exist in C and this question is really about C.)
In C language a struct type does not introduce its own scope. There's no such naming syntax as SomeStruct::a in C language. For this reason there's simply no reason to have static members in structs. You can declare a global variable instead and achieve the same effect. Call your global variable str_a to convey the intent to "associate" it with struct str and just think of that variable as a pseudo-static member of struct str.
Formally speaking, one could do it the same way in C++, i.e. completely ignore this feature of C++ language and use global functions and variables instead of static function and variables inside classes. However, by doing that one would forsake all member access control features of C++. And these features are really worth having. C language has no access control features, meaning that in C one loses [almost] nothing.
The language just doesn't allow it. There's no deeper reason other than that it's not part of the design. You can always achieve the same behaviour with a separate global variable like this:
struct str
{
int b;
} s;
int str_a;
Note that it would be something entirely different to have a non-static int a; inside your struct, which would be a distinct subelement of every object of type struct str.
(Note also that in C++, a language evolved from C, static class members do exist and behave exactly like the workaround I described above, only that the name of the global variable is tightly associated to the name of the class.)
A static modifier is to declare your variable in the global scope in your file and a static modifier in a function creates a variable with a persistant value limited to the scope of this functions. And you can not share the value of this integer between your instances of your struct.
This is not and cannot be supported in C ;)
Why do you want to use a static member in a struct? maybe there is (there must be) a better soluation.
You have good answers here: http://cboard.cprogramming.com/c-programming/123691-static-variable-structure.html
generally speaking you don't have any gain from declaring it static, but if you still wish to it , you may migrate to c++ or declare the whole struct as static.
No, not in C. I believe C++ can do this and it means there is one copy of a that is shared amongst all instances of the struct str structure.
If you want to do something similar in C, you have a few options (there may be more, I just can't think of them at the moment).
The first is to break out the common variable with something like:
int struct_str_static_a;
struct str {
int b;
} s;
That way, there is only one copy of a shared by all instances of the structure - each instance still gets its own copy of b.
A slight modification to that is to introduce a pointer to that common variable and initialise the pointer:
int struct_str_static_a;
struct str {
int *pA;
int b;
} s;
:
s.pA = &struct_str_static_a;
Then you can use *(s.pA) where before you would have used s.a. Because every instance of struct str has its own pA pointer that points to a single a, that gives you a similar effect. However, it's a torturous road to follow.
The third option is to get yourself on the next ISO C working group and put this forward as a change to the language. However, that's going to require a fair bit of effort from yourself for the next ten years or so, probably not worth the effort :-)
This is what I want to do:
1) I want a function that instantiates a data structure.
void instantiateCDB(void);
2) I also want a function that updates the data structure that is instantiated and returns a const pointer to the data structure (to make it read-only)
I know that this can be done in C++/Java. But can it also be done in C?
The program flow that I want to write is:
main(){
instantiateCDB(); // Allocates a CDB
const struct canDataBlock * cdb = getUpdateSystem();
}
// But the best function definitions that I can come up with is this.
struct canDataBlock * instantiateCDB() {
static struct canDataBlock cdb = {0};
return &cdb;
}
const struct canDataBlock * getUpdateSystem() {
struct canDataBlock * cdb = instantiateCDB();
return &cdb;
}
The problem is: How do I access the data structure with write/read access instantiated in the instantiateCDB function if it would be declared void? If I am going to return the allocated data structure, the user can alter the canDataBlock thus losing its integrity. What I want to happen is only the getUpdateSystem() can change the values of the data structure instantiated by the instantiateCDB() function. How do I solve this problem? Is there another technique in C that I do not know about. If there is, please teach me. :)
OO in C can be partly simulated (word "simulation" rather than "implementation" was chosen deliberately), for example inheritance can be simulated by nesting base struct as a first member of "derived" struct, in such case it is possible to "upcast" derived pointer struct to "base" struct as in C there is guarantee that pointer to struct can be used to access pointer to first member.
Depending on the context, you may use opaque data type to hide implementation details. To simulate private and public data you can provide full struct declaration, including all members, but still hide some members as a opaque or void pointer.
struct myclass_privdata;
struct myclass { int data; /* public member */
struct myclass_privdata* data_priv; /* private simulation*/ }
You can also simulate member functions by using function pointers, but you still need to pass explicitly object reference (and initialize it too).
struct S;
void do(struct S* this_p) { }
struct S { void (*do_smth)(struct s* this_p); } s;
s->do_smth = do;
s->do_smth(s);
Probably you can found inspiration in glib library object model. Also there is even a book about this topic.
However, the best advice existing for this topic, is to stay with the language style, rather than trying to do things the language is not supposed to be used (for example, implementing oop in C often results in boilerplate code which adds low functional value - it makes code looking like it is written in OOP when in fact it is not, and makes code looking ugly for those who program in "native" style).
Addressing your question.
1) Function returning data instance is typically implemented as function which returns malloced() pointer
#include <stdlib.h>
struct S {int i; };
struct S* S_alloc(void) { return malloc(sizeof(S)); }
Your code which returns pointer to static data is almost for sure a trouble as all references will refer to same object. And judging by void instantiateCDB(void); declaration I doubt it does really what you want to do.
2) I doubt that you really "want a function that updates the data structure that is instantiated and returns a const pointer to the data structure (to make it read-only)" - because caller will still have a non-const pointer which was just passed to such function.
There's a pattern called opaque data pointer, it might help you here. See http://en.wikipedia.org/wiki/Opaque_data_type.
How can one create a Haskell/C++ style constructor in C? Also, once I have this new object how can I define operations in it (such as LinkedList/Tree)?
C has no language support for objected-oriented concepts such as constructors.
You could manually implement this, along the lines of:
typedef struct
{
int field1;
int field2;
} MyObject;
MyObject *MyObject_new(int field1, int field2)
{
MyObject *p = malloc(sizeof(*p));
if (p != NULL)
{
// "Initialise" fields
p->field1 = field1;
p->field2 = field2;
}
return p;
}
void MyObject_delete(MyObject *p)
{
// Free other resources here too
if (p != NULL)
{
free(p);
}
}
void MyObject_doSomethingInteresting(const MyObject *p)
{
printf("How interesting: %d\n", p->field1 * p->field2);
}
If you want to get really advanced, you can use hideously-complex macros and function pointers to emulate polymorphism and all sorts (at the expense of type-safety); see this book.
See also this question.
See also this FAQ answer if you're interested in compiling object-oriented C++ into C.
I suspect that #Oli Charlesworth's answer is what you really wanted here, but for completeness' sake, if you really want Haskell-style constructors:
Nullary data constructors like True and False can be represented easily by regular enum types. To stay accurate to Haskell you'll want to pretend that you can't treat them as integer values, unless you're also pretending that your Haskell type is an instance of Enum.
Data constructors for product types like (True, "abc") can be represented by functions that take appropriate arguments and return an appropriate struct. Haskell's "record syntax" can be imitated using a struct because C-style structs are already being imitated by Haskell's record syntax. Yay, recursion!
Data constructors for sum types like Nothing and Just 5 can be imitated by a union. To stay accurate to Haskell you'll need to "tag" the union to distinguish the cases safely, such as using a struct and an enum. Think of the | in Haskell's data declarations as indicating an untagged union, with the individual constructors like Nothing and Just being nullary constructors required to be the first element of a product type forming each branch. This is slightly more complicated to implement than you'd think after realizing that it's simpler than it sounds at first.
Type constructors don't really translate well to C. You might be able to fake it badly, if you want to, using macros and such. But you probably don't want to, so stick to monomorphic code.
If you want to put any of the above into practice in actual code, well... good luck and godspeed.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Assuming I have to use C (no C++ or object oriented compilers) and I don't have dynamic memory allocation, what are some techniques I can use to implement a class, or a good approximation of a class? Is it always a good idea to isolate the "class" to a separate file? Assume that we can preallocate the memory by assuming a fixed number of instances, or even defining the reference to each object as a constant before compile time. Feel free to make assumptions about which OOP concept I will need to implement (it will vary) and suggest the best method for each.
Restrictions:
I have to use C and not an OOP
because I'm writing code for an
embedded system, and the compiler and
preexisting code base is in C.
There is no dynamic memory allocation
because we don't have enough memory
to reasonably assume we won't run out
if we start dynamically allocating
it.
The compilers we work with have no problems with function pointers
That depends on the exact "object-oriented" feature-set you want to have. If you need stuff like overloading and/or virtual methods, you probably need to include function pointers in structures:
typedef struct {
float (*computeArea)(const ShapeClass *shape);
} ShapeClass;
float shape_computeArea(const ShapeClass *shape)
{
return shape->computeArea(shape);
}
This would let you implement a class, by "inheriting" the base class, and implementing a suitable function:
typedef struct {
ShapeClass shape;
float width, height;
} RectangleClass;
static float rectangle_computeArea(const ShapeClass *shape)
{
const RectangleClass *rect = (const RectangleClass *) shape;
return rect->width * rect->height;
}
This of course requires you to also implement a constructor, that makes sure the function pointer is properly set up. Normally you'd dynamically allocate memory for the instance, but you can let the caller do that, too:
void rectangle_new(RectangleClass *rect)
{
rect->width = rect->height = 0.f;
rect->shape.computeArea = rectangle_computeArea;
}
If you want several different constructors, you will have to "decorate" the function names, you can't have more than one rectangle_new() function:
void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
rectangle_new(rect);
rect->width = width;
rect->height = height;
}
Here's a basic example showing usage:
int main(void)
{
RectangleClass r1;
rectangle_new_with_lengths(&r1, 4.f, 5.f);
printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
return 0;
}
I hope this gives you some ideas, at least. For a successful and rich object-oriented framework in C, look into glib's GObject library.
Also note that there's no explicit "class" being modelled above, each object has its own method pointers which is a bit more flexible than you'd typically find in C++. Also, it costs memory. You could get away from that by stuffing the method pointers in a class structure, and invent a way for each object instance to reference a class.
I had to do it once too for a homework. I followed this approach:
Define your data members in a
struct.
Define your function members that
take a pointer to your struct as
first argument.
Do these in one header & one c.
Header for struct definition &
function declarations, c for
implementations.
A simple example would be this:
/// Queue.h
struct Queue
{
/// members
}
typedef struct Queue Queue;
void push(Queue* q, int element);
void pop(Queue* q);
// etc.
///
If you only want one class, use an array of structs as the "objects" data and pass pointers to them to the "member" functions. You can use typedef struct _whatever Whatever before declaring struct _whatever to hide the implementation from client code. There's no difference between such an "object" and the C standard library FILE object.
If you want more than one class with inheritance and virtual functions, then it's common to have pointers to the functions as members of the struct, or a shared pointer to a table of virtual functions. The GObject library uses both this and the typedef trick, and is widely used.
There's also a book on techniques for this available online - Object Oriented Programming with ANSI C.
C Interfaces and Implementations: Techniques for Creating Reusable Software, David R. Hanson
http://www.informit.com/store/product.aspx?isbn=0201498413
This book does an excellent job of covering your question. It's in the Addison Wesley Professional Computing series.
The basic paradigm is something like this:
/* for data structure foo */
FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);
you can take a look at GOBject. it's an OS library that give you a verbose way to do an object.
http://library.gnome.org/devel/gobject/stable/
I will give a simple example of how OOP should be done in C. I realize this thread is from 2009 but would like to add this anyway.
/// Object.h
typedef struct Object {
uuid_t uuid;
} Object;
int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);
/// Person.h
typedef struct Person {
Object obj;
char *name;
} Person;
int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);
/// Object.c
#include "object.h"
int Object_init(Object *self)
{
self->uuid = uuid_new();
return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
return self->uuid;
}
int Object_clean(Object *self)
{
uuid_free(self->uuid);
return 0;
}
/// Person.c
#include "person.h"
int Person_init(Person *self, char *name)
{
Object_init(&self->obj); // Or just Object_init(&self);
self->name = strdup(name);
return 0;
}
int Person_greet(Person *self)
{
printf("Hello, %s", self->name);
return 0;
}
int Person_clean(Person *self)
{
free(self->name);
Object_clean(self);
return 0;
}
/// main.c
int main(void)
{
Person p;
Person_init(&p, "John");
Person_greet(&p);
Object_get_uuid(&p); // Inherited function
Person_clean(&p);
return 0;
}
The basic concept involves placing the 'inherited class' at the top of the struct. This way, accessing the first 4 bytes in the struct also accesses the first 4 bytes in the 'inherited class' (assuming non-crazy optimizations). Now, when the pointer of the struct is cast to the 'inherited class', the 'inherited class' can access the 'inherited values' in the same way it would access its members normally.
This and some naming conventions for constructors, destructors, allocation, and deallocation functions (I recommend _init, _clean, _new, and _free) will get you a long way.
As for Virtual functions, use function pointers in the struct, possibly with Class_func(...); wrapper too.
As for (simple) templates, add a size_t parameter to determine size, require a void* pointer, or require a 'class' type with just the functionality you care about. (e.g. int GetUUID(Object *self); GetUUID(&p);)
Use a struct to simulate the data members of a class. In terms of method scope you can simulate private methods by placing the private function prototypes in the .c file and the public functions in the .h file.
GTK is built entirely on C and it uses many OOP concepts. I have read through the source code of GTK and it is pretty impressive, and definitely easier to read. The basic concept is that each "class" is simply a struct, and associated static functions. The static functions all accept the "instance" struct as a parameter, do whatever then need, and return results if necessary. For Example, you may have a function "GetPosition(CircleStruct obj)". The function would simply dig through the struct, extract the position numbers, probably build a new PositionStruct object, stick the x and y in the new PositionStruct, and return it. GTK even implements inheritance this way by embedding structs inside structs. pretty clever.
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <uchar.h>
/**
* Define Shape class
*/
typedef struct Shape Shape;
struct Shape {
/**
* Variables header...
*/
double width, height;
/**
* Functions header...
*/
double (*area)(Shape *shape);
};
/**
* Functions
*/
double calc(Shape *shape) {
return shape->width * shape->height;
}
/**
* Constructor
*/
Shape _Shape() {
Shape s;
s.width = 1;
s.height = 1;
s.area = calc;
return s;
}
/********************************************/
int main() {
Shape s1 = _Shape();
s1.width = 5.35;
s1.height = 12.5462;
printf("Hello World\n\n");
printf("User.width = %f\n", s1.width);
printf("User.height = %f\n", s1.height);
printf("User.area = %f\n\n", s1.area(&s1));
printf("Made with \xe2\x99\xa5 \n");
return 0;
};
In your case the good approximation of the class could be the an ADT. But still it won't be the same.
My strategy is:
Define all code for the class in a separate file
Define all interfaces for the class in a separate header file
All member functions take a "ClassHandle" which stands in for the instance name (instead of o.foo(), call foo(oHandle)
The constructor is replaced with a function void ClassInit(ClassHandle h, int x, int y,...) OR ClassHandle ClassInit(int x, int y,...) depending on the memory allocation strategy
All member variables are store as a member of a static struct in the class file, encapsulating it in the file, preventing outside files from accessing it
The objects are stored in an array of the static struct above, with predefined handles (visible in the interface) or a fixed limit of objects that can be instantiated
If useful, the class can contain public functions that will loop through the array and call the functions of all the instantiated objects (RunAll() calls each Run(oHandle)
A Deinit(ClassHandle h) function frees the allocated memory (array index) in the dynamic allocation strategy
Does anyone see any problems, holes, potential pitfalls or hidden benefits/drawbacks to either variation of this approach? If I am reinventing a design method (and I assume I must be), can you point me to the name of it?
Also see this answer and this one
It is possible. It always seems like a good idea at the time but afterwards it becomes a maintenance nightmare. Your code become littered with pieces of code tying everything together. A new programmer will have lots of problems reading and understanding the code if you use function pointers since it will not be obvious what functions is called.
Data hiding with get/set functions is easy to implement in C but stop there. I have seen multiple attempts at this in the embedded environment and in the end it is always a maintenance problem.
Since you all ready have maintenance issues I would steer clear.
My approach would be to move the struct and all primarily-associated functions to a separate source file(s) so that it can be used "portably".
Depending on your compiler, you might be able to include functions into the struct, but that's a very compiler-specific extension, and has nothing to do with the last version of the standard I routinely used :)
The first c++ compiler actually was a preprocessor which translated the C++ code into C.
So it's very possible to have classes in C.
You might try and dig up an old C++ preprocessor and see what kind of solutions it creates.
Do you want virtual methods?
If not then you just define a set of function pointers in the struct itself. If you assign all the function pointers to standard C functions then you will be able to call functions from C in very similar syntax to how you would under C++.
If you want to have virtual methods it gets more complicated. Basically you will need to implement your own VTable to each struct and assign function pointers to the VTable depending on which function is called. You would then need a set of function pointers in the struct itself that in turn call the function pointer in the VTable. This is, essentially, what C++ does.
TBH though ... if you want the latter then you are probably better off just finding a C++ compiler you can use and re-compiling the project. I have never understood the obsession with C++ not being usable in embedded. I've used it many a time and it works is fast and doesn't have memory problems. Sure you have to be a bit more careful about what you do but its really not that complicated.
C isn't an OOP language, as your rightly point out, so there's no built-in way to write a true class. You're best bet is to look at structs, and function pointers, these will let you build an approximation of a class. However, as C is procedural you might want to consider writing more C-like code (i.e. without trying to use classes).
Also, if you can use C, you can probally use C++ and get classes.