Encapsulating access to similar structures - c

My application is written in C. I have a module that uses some data from a certain given global structure. I now have to extend the module to optionally work against a different given global structure, which basically provides the same fields (as far as my module is concerned), but under different names.
Here's a car analogy to hopefully make my problem clearer. I've got these two global structures I have no control over.
struct {
unsigned char manufacturer_id;
unsigned short top_speed;
} Car;
struct {
RGB_t color;
unsigned short topSpeed;
unsigned char mfr;
} Automobile;
Let's say my Car Manager module uses information from Automobile. E.g.,
const char *car_manager__get_manufacturer_name(car_manager_t *self)
{
return self->manufacturers[Automobile.mfr];
}
I'd like to extend Car Manager to optionally (perhaps decided by a flag in the car_manager_t instance) use the same information from Car, so the above function would return self->manufacturers[Car.manufacturer_id]. I don't want to duplicate any logic in the module while adding this functionality.
I assume I'll have to put an interface on the access to the global structures. Any suggestions on how to do that?

I would define functions for getting the needed values, and pass pointers to the functions. You could even pass a struct which contains the needed function pointers.
struct Interface {
unsigned char (*manufacturer)(void);
unsigned short (*top_speed)(void);
}
struct Interface CarInterface = {&Car_manufacturer, &Car_top_speed};
struct Interface AutoInterface = {&Auto_manufacturer, &Auto_top_speed};
const char *car_manager__get_manufacturer_name(car_manager_t *self, Interface i)
{
return self->manufacturers[(*i.manufacturer)()];
}
I haven't written any C for a long time; please correct my syntax if necessary!

I don't know exactly what do you need, but note that if you have an union, that contain several structs that begin with the same types, you can access those types equally through all the structs. for example, if you have:
union bla {
struct {
int a;
char b;
float *c;
} s1;
struct {
int r;
char c;
float *j;
short s;
} s2;
int i;
} un;
Then un.s1.a,un.s2.r,un.i are the same, and so on un.s1.c==un.s2.j
Also, consider moving to C++ (and overload functions for your structs)

This is just the first solution that came to my mind, but one way to go about it might be to generalize your module to make it configurable so you can tell it how to look up the fields.
It's been a while since I coded in C, so consider this pseudo code ;)
The two structures:
/* Struct layout 1 */
struct {
float x; /*aka foo*/
float y; /*aka bar*/
float z; /*aka baz*/
} entity_type1;
/* Struct layout 2 */
struct {
float c; /*aka baz*/
float a; /*aka foo*/
float b; /*aka bar*/
} entity_type2;
The module:
struct {
int foo_index;
int bar_index;
int baz_index;
} fields_definition;
/* Private configuration */
fields_definition entity_fields;
/* Private getters */
float foo(void * entity) {
return *(float*)(entity_ptr + entity_fields.foo_index);
}
float bar(void * entity) {
return *(float*)(entity_ptr + entity_fields.bar_index);
}
/* Private setters */
void baz(void * entity, float value) {
*(float*)(entity_ptr + entity_fields.baz_index) = value; /* Legal?? */
}
/* Exported/Public function for setup */
void configure(fields_definition entity_fields_config){
entity_fields = entity_fields_config;
}
/* Normal exported/public function for usage */
void some_operation(void * entity) {
baz(entity, foo(entity) + bar(entity));
}
Usage:
/* Initialize... */
fields_definition for_type1 = {0,4,8};
fields_definition for_type2 = {4,8,0};
configure(for_type2);
/* ... and use */
entity_type2 e;
some_operation(&e);
Setting up the field_definition(s) could also be done by something similar to
entity_type2 t2;
fields_definition for_type2 = {
&(t2.a)-&t2,
&(t2.b)-&t2,
&(t2.c)-&t2
};
(Again, it's been a while, so I don't remember exactly how this would be done.) I believe some compilers have a built in function for getting the field offset in a struct, which would be cleaner but less portable.

Related

Structs of two different types - need to pass one to a function dynamically

Apologies in advance, I have only begun writing C 2 months ago so this is all quite new to me.
I am working on a codebase which already had a solution implemented that needs to be changed.
Currently, one struct holds all the variables for both read/write functionality. This needs to be modified to be two different structs, one for read, one for write.
Below is the current implementation.
typedef struct Foo_s
{
int x;
int y;
int z;
}
BarFunction(Foo_s foo) {
//logic
}
Here is the changes that I need to do
typedef struct FooRead_s
{
int x;
int y;
}
typedef struct FooWrite_s
{
int z;
}
The problem occurs when BarFunction(Foo_s foo) is called. It needs to be able to accept either FooRead_s or FooWrite_s at any given time, even though they are different. How do I allow BarFunction to accept both FooRead_s and FooWrite_s?
There are no way in C to distinguish between two or more structures without some selector parameter. One can use union of structs FooRead_s, FooWrite_s and operation selector.
Selector can be passed as separate argument
typedef union {
FooRead_s rs;
FooWrite_s ws;
} Foo_params;
void BarFunction(bool write, Foo_params *foo) {
if (write) {
// use foo->ws;
}
// ...
}
or embedded into structure
typedef struct {
bool write;
union {
FooRead_s rs;
FooWrite_s ws;
} params;
} Foo_action;
void BarFunction(Foo_action *foo) {
if (foo->write) {
// use foo->params.ws;
}
}
If we correct your typedefs and write the function to accept a struct passed by pointer, as it ought to be designed, then we end up with this:
typedef struct
{
int x;
int y;
} FooRead_s;
typedef struct
{
int z;
} FooWrite_s;
void BarFunction_Read (FooRead_s* foo) {
//logic
}
void BarFunction_Write (FooWrite_s* foo) {
//logic
}
Now if you have the option to use the standard C language, you can then write the function call with generic programming:
#define BarFunction(foo) \
_Generic((foo), \
FooRead_s*: BarFunction_Read, \
FooWrite_s*: BarFunction_Write)(foo)
int main (void)
{
FooRead_s read = {0};
FooWrite_s write = {0};
BarFunction(&read);
BarFunction(&write);
//BarFunction(read); compiler error here as we should have
}

C generic type as function argument input

So I have two different structs in which all the properties that I will be accessing will be the same. and I also have a function, who's argument, i want to be able to accept either of the two. Example:
typedef struct{
int whatnot = 14;
int thing[11];
} TH_CONFIG;
typedef struct{
int whatnot = 3;
int thing[5];
} TH_CONFIG_2;
*_CONFIG var;
void fun(*_CONFIG input)
{
input.whatnot = 5;
}
int main(){
fun(var);
}
I may have an inkling that I should use void as the type from that I could typecast or something?, but my searching has only yielded things about function pointers, templates, and C#.
EDIT: *_CONFIG is not meant to be syntactically correct, its signifying that I don't know what to do there, but its supposed to be the _CONFIG type
Possible solutions.
Just use an array of length 11 for both of them. Did you really run out of those last 6 bytes on your OS?
Make it a dynamic array.
Just write in assembly, you clearly don't care about C's higher-level-ness.
Use a language like C++ that supports templates or polymorphism.
Just pass in the arguments of the struct you care about.
void fun(int* whatnot) {
*whatnot = 5;
}
int main() {
fun(&myStruct.whatnot);
return 0;
}
Factor into a quasi-OO design.
struct {
int whatnot;
} typedef Common;
struct TH_CONFIG_1 {
Common common;
int thing[11];
};
struct TH_CONFIG_2 {
Common common;
int thing[5];
}
But if you insist...
void fun(void* input) {
( (int)(*input) ) = 5;
}
or...
void fun(void* input) {
( (TH_CONFIG*) input)->whatnot = 5; // may have been a TH_CONFIG_2, but who cares?
}
Note: this would not pass code review at any C shop.
You can use any pointer type and cast it.
If all the properties you're accessing are the same, I'm guessing one's an extension of the other (since the properties need to have the same offset from the beginning of the struct). In that case you may want to use this pattern:
struct base {
int foo;
char **strings;
};
struct extended {
struct base super;
double other_stuff;
};
Since super is at the start of struct extended, you can cast a struct extended * to struct base * without problems. Of course, you could do that by repeating the same fields in the beginning of struct extended instead, but then you're repeating yourself.

When are anonymous structs and unions useful in C11?

C11 adds, among other things, 'Anonymous Structs and Unions'.
I poked around but could not find a clear explanation of when anonymous structs and unions would be useful. I ask because I don't completely understand what they are. I get that they are structs or unions without the name afterwards, but I have always (had to?) treat that as an error so I can only conceive a use for named structs.
Anonymous union inside structures are very useful in practice. Consider that you want to implement a discriminated sum type (or tagged union), an aggregate with a boolean and either a float or a char* (i.e. a string), depending upon the boolean flag. With C11 you should be able to code
typedef struct {
bool is_float;
union {
float f;
char* s;
};
} mychoice_t;
double as_float(mychoice_t* ch)
{
if (ch->is_float) return ch->f;
else return atof(ch->s);
}
With C99, you'll have to name the union, and code ch->u.f and ch->u.s which is less readable and more verbose.
Another way to implement some tagged union type is to use casts. The Ocaml runtime gives a lot of examples.
The SBCL implementation of Common Lisp does use some union to implement tagged union types. And GNU make also uses them.
A typical and real world use of anonymous structs and unions are to provide an alternative view to data. For example when implementing a 3D point type:
typedef struct {
union{
struct{
double x;
double y;
double z;
};
double raw[3];
};
}vec3d_t;
vec3d_t v;
v.x = 4.0;
v.raw[1] = 3.0; // Equivalent to v.y = 3.0
v.z = 2.0;
This is useful if you interface to code that expects a 3D vector as a pointer to three doubles. Instead of doing f(&v.x) which is ugly, you can do f(v.raw) which makes your intent clear.
struct bla {
struct { int a; int b; };
int c;
};
the type struct bla has a member of a C11 anonymous structure type.
struct { int a; int b; } has no tag and the object has no name: it is an anonymous structure type.
You can access the members of the anonymous structure this way:
struct bla myobject;
myobject.a = 1; // a is a member of the anonymous structure inside struct bla
myobject.b = 2; // same for b
myobject.c = 3; // c is a member of the structure struct bla
Another useful implementation is when you are dealing with rgba colors, since you might want access each color on its own or as a single int.
typedef struct {
union{
struct {uint8_t a, b, g, r;};
uint32_t val;
};
}Color;
Now you can access the individual rgba values or the entire value, with its highest byte being r. i.e:
int main(void)
{
Color x;
x.r = 0x11;
x.g = 0xAA;
x.b = 0xCC;
x.a = 0xFF;
printf("%X\n", x.val);
return 0;
}
Prints 11AACCFF
I'm not sure why C11 allows anonymous structures inside structures. But Linux uses it with a certain language extension:
/**
* struct blk_mq_ctx - State for a software queue facing the submitting CPUs
*/
struct blk_mq_ctx {
struct {
spinlock_t lock;
struct list_head rq_lists[HCTX_MAX_TYPES];
} ____cacheline_aligned_in_smp;
/* ... other fields without explicit alignment annotations ... */
} ____cacheline_aligned_in_smp;
I'm not sure if that example strictly necessary, except to make the intent clear.
EDIT: I found another similar pattern which is more clear-cut. The anonymous struct feature is used with this attribute:
#if defined(RANDSTRUCT_PLUGIN) && !defined(__CHECKER__)
#define __randomize_layout __attribute__((randomize_layout))
#define __no_randomize_layout __attribute__((no_randomize_layout))
/* This anon struct can add padding, so only enable it under randstruct. */
#define randomized_struct_fields_start struct {
#define randomized_struct_fields_end } __randomize_layout;
#endif
I.e. a language extension / compiler plugin to randomize field order (ASLR-style exploit "hardening"):
struct kiocb {
struct file *ki_filp;
/* The 'ki_filp' pointer is shared in a union for aio */
randomized_struct_fields_start
loff_t ki_pos;
void (*ki_complete)(struct kiocb *iocb, long ret, long ret2);
void *private;
int ki_flags;
u16 ki_hint;
u16 ki_ioprio; /* See linux/ioprio.h */
unsigned int ki_cookie; /* for ->iopoll */
randomized_struct_fields_end
};
Well, if you declare variables from that struct only once in your code, why does it need a name?
struct {
int a;
struct {
int b;
int c;
} d;
} e,f;
And you can now write things like e.a,f.d.b,etc.
(I added the inner struct, because I think that this is one of the most usages of anonymous structs)

void pointers and function pointers in structure members

Hello I have following code.
typedef struct __vector {
int (*container_end) ( struct __vector *);
}vector;
and another iterator structure with following declaration :
typedef struct __iterator {
void *ptr_to_container;
int (*end)(struct __iterator *);
}iterator;
int
end(iterator *itr) {
return (itr->ptr_to_container)->container_end(itr->ptr_to_container);
}
This code does not compile as ptr_to_container is void pointer.
Is there any work-around to this problem.
container_end function will be defined separately and ptr_to_container will point to some container.
thanks
Avinash
It looks like you have missed something when defining the iterator structure. Why does the iterator have a function pointer to an 'end' function that accepts an iterator?
If you want it to be really generic, you could perhaps use this definition instead:
typedef struct __iterator {
void * ptr_to_container;
int (*end)(void *);
} iterator;
int end(iterator * it) { return it->end(it->ptr_to_container)); }
In the vector definition (and other data types), you can then define a function to create an iterator:
static int vector_end(vector * v) { /* implementation omittted */ }
iterator * vector_create_iterator(vector * v)
{
iterator * it = malloc(sizeof(iterator));
it->ptr_to_container = v;
it->end = vector_end;
return it;
}
However, the solution really depends on how the data structures are defined. In the above suggestion, it is up to each data structure to provide an implementation for how to traverse it.
As an alternative, you could set up a generic data structure interface, like
typedef struct _container container;
struct _container {
int (*end)(container * c);
};
Then the vector implementation would "only" have to fill in this container structure:
typedef struct _vector {
container c;
/* other fields required by the vector */
}
static int vector_end(container * c)
{
vector * v = (vector *) c;
...
}
container * create_vector()
{
vector * v = malloc(sizeof(vector));
v->c.end = vector_end;
return v;
}
...and the iterator could work with just the generic container:
typedef struct _iterator {
container * c;
/* other fields used by the iterator, such as current position */
}
int end(iterator * it) { return it->c->end(it->c); }
From the code sample in the question, it looks almost like you have mixed up these two approaches :-)
Did you try casting to a vector *?
return ((vector *)(itr->ptr_to_container))->containter_end(itr->ptr_to_container);
However, are you sure you want to do this? You are using itr to call a function and then pass itr to that function. Including more context (more code) would help.
You need to explicitly cast *ptr_to_container to a vector pointer:
((__vector *)(itr->ptr_to_container))->container_end
Otherwise the compiler doesn't know what is the structure of the target.
Though, I don't really see why you want to have such a construction. It looks like you want to have object orientation here with inheritance, but without explicitly stating anything. It won't work well. In C, you'll have to use less general structures, or move to C++.
If it must be void * use
int
end(iterator *itr) {
return ((vector)(itr->ptr_to_container))->container_end(itr->ptr_to_container);
}
or else specify in the iterator that it is a vector iterator
typedef struct __iterator {
vector *ptr_to_container;
int (*end)(struct __iterator *);
}iterator; //probably you'll need to rename to make type of iterator clear
If you need to keep the abstraction (one iterator for all of you containers) nothing comes to mind atm...

How can I simulate OO-style polymorphism in C?

Is there a way to write OO-like code in the C programming language?
See also:
Can you write object-oriented code in C?
Object-orientation in C
Found by searching on "[c] oo".
The first C++ compiler ("C with classes") would actually generate C code, so that's definitely doable.
Basically, your base class is a struct; derived structs must include the base struct at the first position, so that a pointer to the "derived" struct will also be a valid pointer to the base struct.
typedef struct {
data member_x;
} base;
typedef struct {
struct base;
data member_y;
} derived;
void function_on_base(struct base * a); // here I can pass both pointers to derived and to base
void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class
The functions can be part of the structure as function pointers, so that a syntax like p->call(p) becomes possible, but you still have to explicitly pass a pointer to the struct to the function itself.
Common approach is to define struct with pointers to functions. This defines 'methods' which can be called on any type. Subtypes then set their own functions in this common structure, and return it.
For example, in linux kernel, there is struct:
struct inode_operations {
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
struct dentry * (*lookup) (struct inode *,struct dentry *,
struct nameidata *);
...
};
Each registered type of filesystem then registers its own functions for create, lookup, and remaining functions. Rest of code can than use generic inode_operations:
struct inode_operations *i_op;
i_op -> create(...);
C++ is not that far from C.
Classes are structures with a hidden pointer to a table of function pointers called VTable. The Vtable itself is static.
When types point to Vtables with the same structure but where pointers point to other implementation, you get polymorphism.
It is recommended to encapsulate the calls logic in function that take the struct as parameter to avoid code clutter.
You should also encapsulcte structures instantiation and initialisation in functions (this is equivalent to a C++ constructor) and deletion (destructor in C++). These are good practice anyway.
typedef struct
{
int (*SomeFunction)(TheClass* this, int i);
void (*OtherFunction)(TheClass* this, char* c);
} VTable;
typedef struct
{
VTable* pVTable;
int member;
} TheClass;
To call the method:
int CallSomeFunction(TheClass* this, int i)
{
(this->pVTable->SomeFunction)(this, i);
}
I looked at everyone elses' answers and came up with this:
#include <stdio.h>
typedef struct
{
int (*get)(void* this);
void (*set)(void* this, int i);
int member;
} TheClass;
int Get(void* this)
{
TheClass* This = (TheClass*)this;
return This->member;
}
void Set(void* this, int i)
{
TheClass* This = (TheClass*)this;
This->member = i;
}
void init(TheClass* this)
{
this->get = &Get;
this->set = &Set;
}
int main(int argc, char **argv)
{
TheClass name;
init(&name);
(name.set)(&name, 10);
printf("%d\n", (name.get)(&name));
return 0;
}
I hope that answers some questions.
Appendix B of the article Open Reusable Object Models, by Ian Piumarta and Alessandro Warth of VPRI is an implementation of an Object model in GNU C, about 140 lines of code. It's a fascinating read !
Here's the uncached version of the macro that sends messages to objects, using a GNU extension to C (statement expression):
struct object;
typedef struct object *oop;
typedef oop *(*method_t)(oop receiver, ...);
//...
#define send(RCV, MSG, ARGS...) ({ \
oop r = (oop)(RCV); \
method_t method = _bind(r, (MSG)); \
method(r, ##ARGS); \
})
In the same doc, have a look at the object, vtable, vtable_delegated and symbol structs, and the _bind and vtable_lookup functions.
Cheers!
What I usually like to do is to wrap the structs in another which contain meta information about the wrapped class and then build visitor like function lists acting on the generic struct. The advantage of this approach is that you don't need to modify the existing structures and you can create visitors for any subset of structs.
Take the usual example:
typedef struct {
char call[7] = "MIAO!\n";
} Cat;
typedef struct {
char call[6] = "BAU!\n";
} Dog;
We can wrap the 2 strutures in this new structure:
typedef struct {
const void * animal;
AnimalType type;
} Animal;
The type can be a simple int but let's not be lazy:
typedef enum {
ANIMAL_CAT = 0,
ANIMAL_DOG,
ANIMAL_COUNT
} AnimalType;
It would be nice to have some wrapping functions:
Animal catAsAnimal(const Cat * c) {
return (Animal){(void *)c, ANIMAL_CAT};
}
Animal dogAsAnimal(const Dog * d) {
return (Animal){(void *)d, ANIMAL_DOG};
}
Now we can define our "visitor":
void catCall ( Animal a ) {
Cat * c = (Cat *)a.animal;
printf(c->call);
}
void dogCall ( Animal a ) {
Dog * d = (Dog *)a.animal;
printf(d->call);
}
void (*animalCalls[ANIMAL_COUNT])(Animal)={&catCall, &dogCall};
Then the actual usage will be:
Cat cat;
Dog dog;
Animal animals[2];
animals[0] = catAsAnimal(&cat);
animals[1] = dogAsAnimal(&dog);
for (int i = 0; i < 2; i++) {
Animal a = animals[i];
animalCalls[a.type](a);
}
The disadvantage of this approach is that you have to wrap the structures every time you want to use it as a generic type.
The file functions fopen, fclose, fread are examples of OO code in C. Instead of the private data in class, they work on the FILE structure which is used to encapsulate the data and the C functions acts as an member class functions.
http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016
#include <stdio.h>
typedef struct {
int x;
int z;
} base;
typedef struct {
base;
int y;
int x;
} derived;
void function_on_base( base * a) // here I can pass both pointers to derived and to base
{
printf("Class base [%d]\n",a->x);
printf("Class base [%d]\n",a->z);
}
void function_on_derived( derived * b) // here I must pass a pointer to the derived class
{
printf("Class derived [%d]\n",b->y);
printf("Class derived [%d]\n",b->x);
}
int main()
{
derived d;
base b;
printf("Teste de poliformismo\n");
b.x = 2;
d.y = 1;
b.z = 3;
d.x = 4;
function_on_base(&b);
function_on_base(&d);
function_on_derived(&b);
function_on_derived(&d);
return 0;
}
The output was:
Class base [3]
Class base [1]
Class base [4]
Class derived [2]
Class derived [3]
Class derived [1]
Class derived [4]
so it works, its a polymorphic code.
UncleZeiv explained about it at the beginning.
From Wikipedia:
In programming languages and type theory, polymorphism (from Greek πολύς, polys, "many, much" and μορφή, morphē, "form, shape") is the provision of a single interface to entities of different types.
So I would say the only way to implement it in C is by using variadic arguments along with some (semi)automatic type info management.
For example in C++ you can write (sorry for trivialness):
void add( int& result, int a1, int a2 );
void add( float& result, float a1, float a2 );
void add( double& result, double a1, double a2 );
In C, among other solutions, the best you can do is something like this:
int int_add( int a1, int a2 );
float float_add( float a1, fload a2 );
double double_add( double a1, double a2 );
void add( int typeinfo, void* result, ... );
Then you need:
to implement the "typeinfo" with enums/macros
to implement the latter function with stdarg.h stuff
to say goodbye to C static type checking
I am almost sure that any other implementation of polymorphism should look much like this very one.
The above answers, instead, seems to try to address inheritance more than polymorphism!
In order too build OO functionality in C, you can look at previous answers.
But, (as it has been asked in other questions redirected to this one) if you want to understand what polymorphism is, by examples in C language. Maybe I am wrong, but I can't think of anything as easy to understand as C pointers arithmetic. In my opinion, pointer arithmetic is inherently polymorphic in C. In the following example the same function (method in OO), namely the addition (+), will produce a different behavior depending on the properties of the input structures.
Example:
double a*;
char str*;
a=(double*)malloc(2*sizeof(double));
str=(char*)malloc(2*sizeof(char));
a=a+2; // make the pointer a, point 2*8 bytes ahead.
str=str+2; // make the pointer str, point 2*1 bytes ahead.
Disclaimer: I am very new at C and very much looking forward to being corrected and learn from other user's comments, or even completely erase this answer, should it be wrong. Many thanks,
A very crude example of simple function overloading, much can be achieved using variadic macros.
#include <stdio.h>
#include <stdlib.h>
#define SCOPE_EXIT(X) __attribute__((cleanup (X)))
struct A
{
int a;
};
struct B
{
int a, b;
};
typedef struct A * A_id;
typedef struct B * B_id;
A_id make_A()
{
return (A_id)malloc(sizeof(struct A));
}
void destroy_A(A_id * ptr)
{
free(*ptr);
*ptr = 0;
}
B_id make_B()
{
return (B_id)malloc(sizeof(struct B));
}
void destroy_B(B_id * ptr)
{
free(*ptr);
*ptr = 0;
}
void print_a(A_id ptr)
{
printf("print_a\n");
}
void print_b(B_id ptr)
{
printf("print_b\n");
}
#define print(X) _Generic((X),\
A_id : print_a, \
B_id : print_b\
)(X)
int main()
{
A_id aa SCOPE_EXIT(destroy_A) = make_A();
print(aa);
B_id bb SCOPE_EXIT(destroy_B) = make_B();
print(bb);
return 0;
}
Different implementations of functions is one of the key features of polymorphism, so you must use function pointers.
animal.h
typedef struct Animal {
const void (*jump)(struct Animal *self);
} Animal;
pig.h
#include "animal.h"
typedef struct {
Animal animal_interface;
char *name;
} Pig;
Pig *NewPig(char *name);
pig.c
#include <stdio.h>
#include <stdlib.h>
#include "pig.h"
static void PigJump(Animal *_self) {
Pig *self = (Pig *)_self;
printf("%s Pig jump.\n", self->name);
}
Pig *NewPig(char *name) {
Pig *self = (Pig *)malloc(sizeof(Pig));
self->animal_interface.jump = PigJump;
self->name = name;
return self;
}
main.c
#include "pig.h"
int main() {
Animal *a = &(NewPig("Peppa")->animal_interface);
Animal *b = &(NewPig("Daddy")->animal_interface);
a->jump(a);
b->jump(b);
return 0;
}
Output:
Peppa Pig jump.
Daddy Pig jump.
I have successfully achieved polymorphism in C so I felt like sharing my code. I have a struct Pas which "inherits" from struct Zivotinja (Pas means Dog, Zivotinja means Animal BTW).
In both Zivotinja and Pas the first field of the struct is the vTable.
Zivotinja has a vTable of the type ZivotinjaVTable, Pas has a vTable of the type PasVTable. So, we have
typedef struct ZivotinjaVTableStruct{
void (*ispisiPodatkeOZivotinji)(void *zivotinja);
int (*dajGodine) (void *zivotinja);
} ZivotinjaVTable;
typedef struct ZivotinjaStruct{
ZivotinjaVTable *vTable;
int godine;
} Zivotinja;
and we have
typedef struct PasVTableStruct{
void (*ispisiPodatkeOZivotinji)(void *Pas);
int (*dajGodine) (void *Pas);
bool (*daLiJeVlasnikStariji) (void *Pas);
} PasVTable;
typedef struct PasStruct{
PasVTable *vTable;
int godine;
const char* vlasnik;
int godineVlasnika;
} Pas;
Don't worry about the names of the functions, that's not relevant.
Anyway, I then wrote functions for both of these vTables. How did I connect the vTables with the functions that I wrote for them? I created a global struct both for the ZivotinjaVTable and for the PasVTable. I created vTableZivotinjaGlobal and vTablePasGlobal which have function pointers of the functions that I wrote. Then I created functions Pas_new() and Zivotinja_new() which initialize vTable fields to point to these global vTable structs.
Notice the important details in the code above. The important thing is that vTables are the first fields in their structs. That way, when we write
Zivotinja *z = (Zivotinja*) Pas_new(/* init variables */);
z->vTable->someMethod(z);
the compiler knows that vTable is the first field in the Zivotinja struct, so when compiler reads z->vTable, it will go to the memory address to which the first 8 bytes of your struct z point to (or first 4 bytes, if you have a 32bit PC, but that is irrelevant for the point that I am making).
This is how I tricked the computer, since this z pointer is actually pointing to a Pas struct and since PasVTable *vTable is the first field of the Pas struct, after z->vTable we will actually be at the memory address of the pasVTableGlobal, instead of being at the memory address of the zivotinjaVTableGlobal.
Now, another very important detail, someMethod needs to be at the same spot both in the ZivotinjaVTable and in the PasVTable. What I mean is - if someMethod is the 2nd field in the ZivotinjaVTable then it needs to be the second field of the PasVTable. Why?
Because let's say someMethod is the second field of the ZivotinjaVTable, when the compiler reads z->vTable->someMethod(z); computer will take the second 8 bytes in the memory address z->vTable and it will put those 8 bytes into the instruction pointer (or second 4 bytes if you have a 32 bit PC, but again, this is not relevant). Computer "thinks" it is putting the second 8 bytes of the ZivotinjaVTable into the instruction pointer, but in reality it is putting the second 8 bytes of the PasVTable into the instruction pointer.
This is how the trick works, because the function that we want the computer to execute is also the second field (but of the PasVTable, not ZivotinjaVTable), the computer will "think" that it is executing the second function of the ZivotinjaVTable, but in reality it will be executing the second function of the PasVTable.
So, to recapitulate, vTables should be on the same spot in your structs and your structs should have corresponding methods at the same spots in their vTables.
Same goes for other fields of your structs. The second field of the Zivotinja struct matches the second field of the Pas struct, that way when you write
animal_which_is_actually_a_dog->age = 10;
You will trick the compiler in basically the same way as with vTables (you will trick it in the same way that I have described above).
Here is the entire code, in the main function you can write the following
Zivotinja *zivotinja = Zivotinja_new(10);
zivotinja->vTable->ispisiPodatkeOZivotinji(zivotinja);
Zivotinja *pas = Pas_new_sve(5, 50, "Milojko");
pas->vTable->ispisiPodatkeOZivotinji(pas);
int godine = pas->vTable->dajGodine(pas);
printf("The dog which was casted to an animal is %d years old.\n", godine);
Then this is the code for Zivotinja
typedef struct ZivotinjaVTableStruct{
void (*ispisiPodatkeOZivotinji)(void *zivotinja);
int (*dajGodine) (void *zivotinja);
} ZivotinjaVTable;
typedef struct ZivotinjaStruct{
ZivotinjaVTable *vTable;
int godine;
} Zivotinja;
void ispisiPodatkeOOvojZivotinji(Zivotinja* zivotinja){
printf("Ova zivotinja ima %d godina. \n", zivotinja->godine);
}
int dajGodineOveZivotinje(Zivotinja *z){
return z->godine;
}
struct ZivotinjaVTableStruct zivotinjaVTableGlobal = {ispisiPodatkeOOvojZivotinji, dajGodineOveZivotinje};
Zivotinja* Zivotinja_new(int godine){
ZivotinjaVTable *vTable = &zivotinjaVTableGlobal;
Zivotinja *z = (Zivotinja*) malloc(sizeof(Zivotinja));
z->vTable = vTable;
z->godine = godine;
}
And finally, the code for Pas
typedef struct PasVTableStruct{
void (*ispisiPodatkeOZivotinji)(void *Pas);
int (*dajGodine) (void *Pas);
bool (*daLiJeVlasnikStariji) (void *Pas);
} PasVTable;
typedef struct PasStruct{
PasVTable *vTable;
int godine;
const char* vlasnik;
int godineVlasnika;
} Pas;
void ispisiPodatkeOPsu(void *pasVoid){
Pas *pas = (Pas*)pasVoid;
printf("Pas ima %d godina, vlasnik se zove %s, vlasnik ima %d godina. \n", pas->godine, pas->vlasnik, pas->godineVlasnika);
}
int dajGodinePsa(void *pasVoid){
Pas *pas = (Pas*) pasVoid;
return pas->godine;
}
bool daLiJeVlasnikStariji(Pas *pas){
return pas->godineVlasnika >= pas->godine;
}
struct PasVTableStruct pasVTableGlobal = {
ispisiPodatkeOPsu,
dajGodinePsa,
daLiJeVlasnikStariji
};
Pas* Pas_new(int godine){
Pas *z = (Pas*) malloc(sizeof(Pas));
z->vTable = (&pasVTableGlobal);
}
Pas *Pas_new_sve(int godine, int godineVlasnika, char* imeVlasnika){
Pas *pas = (Pas*) malloc(sizeof(Pas));
pas->godine = godine;
pas->godineVlasnika = godineVlasnika;
pas->vlasnik = imeVlasnika;
pas->vTable = &pasVTableGlobal;
}

Resources