Hi I am currently attempting to learn C and I was wondering if there is a way to attain polymorphism in structures which contain a list of other different type of structures?
An example case of this is as such:
#include <stdlib.h>
#include <stdio.h>
typedef void (*update_t)(void *);
typedef struct entity entity_t;
typedef struct compA compA_t;
typedef struct compB compB_t;
struct compA{
update_t update;
};
struct compB{
update_t update;
};
struct entity{
update_t update;
int curSize;
void **components;
};
void compA_update(void *c){
printf("updating: componentA\n");
}
compA_t *compA_create(){
compA_t *c = malloc(sizeof(compA_t));
c->update = compA_update;
return c;
}
void compB_update(void *c){
printf("updating: componentB\n");
}
compB_t *compB_create(){
compB_t *c = malloc(sizeof(compB_t));
c->update = compB_update;
return c;
}
void entity_update(void *en){
entity_t *e = (entity_t *)en;
for(int i = 0; i < e->curSize; i++){
//would like to somehow update all the components with one line just iterating through the array but does not seem possible
}
return;
}
entity_t *entity_create(){
entity_t *e = malloc(sizeof(entity_t));
e->curSize = 0;
e->update = entity_update;
calloc(32, sizeof(void *));
return e;
}
void add_component(entity_t *e, void *c){
printf("%d\n", e->curSize);
e->components[e->curSize] = c;
e->curSize++;
return;
}
int main(void){
entity_t *e = entity_create();
compA_t *a = compA_create();
compB_t *b = compB_create();
add_component(e, a);
add_component(e, b);
e->update(e);
return 0;
}
So far my approach to this problem has been solved with void pointer arrays of a tuple structure which contains a enum type which identifies the structure as well as the structure itself and then in a potential update function a fairly ugly switch statement has to be implemented with a case for each specific type.
Is there a better way to do this? As the switch approach will get fairly crazy pretty fast if there are a lot of different types within the array. which means one must explicitly add cases for each type and every case does exactly the same thing, which in this case is call a function pointer named "update".
You can try data polymorphism instead of function pointer. That is, different data produce different behavior, using the same code.
For example, a simple polymorphic behavior:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
typedef const char* ccstr;
typedef struct animal_attr_t
{
bool is_body_segmented;
float gill_completeness;
float lung_completeness;
} animal_attr_t;
typedef struct species
{
ccstr name, kingdom, domain;
animal_attr_t animal_attr[0];
} species;
void initialize_species_base(species *this, ccstr name, ccstr kingdom, ccstr domain)
{
this->name = name;
this->kingdom = kingdom;
this->domain = domain;
}
void initialize_animal_attr(animal_attr_t *this, bool is_body_segmented, float gill_completenss, float lung_completeness)
{
this->is_body_segmented = is_body_segmented;
this->gill_completeness = gill_completenss;
this->lung_completeness = lung_completeness;
}
void print_species(species*);
int main(int argc, char *argv[])
{
species *yeast = calloc(sizeof(species), 1);
assert(yeast);
initialize_species_base(yeast, "yeast", "fungus", "eukaryote");
print_species(yeast);
species *dog = calloc(sizeof(species) + sizeof(animal_attr_t), 1);
assert(dog);
initialize_species_base(dog, "dog", "animal", "eukaryote");
initialize_animal_attr(dog->animal_attr, true, 0.0f, 1.0f);
print_species(dog);
free(yeast);
free(dog);
}
void print_species(species *this)
{
printf("name = %s, kingdom = %s, domain = %s",
this->name, this->kingdom, this->domain);
if (strcmp(this->kingdom, "animal") == 0) {
animal_attr_t *ani_attr = this->animal_attr;
printf(", has %s, %f completeness of gill, %f completeness of lung",
ani_attr->is_body_segmented ? "segmented body" : "unsegmented body",
ani_attr->gill_completeness, ani_attr->lung_completeness);
}
printf(".\n");
}
yeast and dog is 2 completely different types, yet with species it is expressed in an unified way and print_species has polymorphic behavior.
Related
I have a c struct that has a const variable.
typedef struct {
u32 status;
const u32 dir_search_idx;} FS_OBJ;
What I would like to do is init the const variable in a function once I have created the struct object. I guess I want to do something similar to what a constructor would do in c++. Is it possible to do something similar in c? Thanks
This should work perfectly fine if you are using C99 or newer and want to initialize the const variable when creating the struct:
FS_OBJ obj = { .status = /* something */, .dir_seach_idx = /* something */ };
You can't modify the const variable after creating the struct. Then you would have to remove the const keyword as mentioned by user3386109 in the comments.
I think const is not the right tool for what you are looking for. You can put data (structs) and behavior (functions) in a *.c file and provide public functions in the corresponding header file. This way you can mimic the equivalent c++ code that you want and hide the data and of course, you can define a constructor. A great book that might help is The GLib/GTK+ Development Platform. In chapter 3 you can find a good introduction to Semi-Object-Oriented Programming in C.
Here is a possible implementation, not necessarily the best one:
/src/main.c
#include <stdio.h>
#include "point.h"
int main()
{
Point *p1 = init(6, 7);
printf("%d\n", getX(p1));
printf("%d\n", getY(p1));
Point *p2 = init(12, 14);
printf("%d\n", getX(p2));
printf("%d\n", getY(p2));
setX(p2, 16);
printf("%d\n", getX(p2));
setY(p2, 16); /* error; we want y to initialize once and remain constant. Also accessing y with p2->y is an error too. */
printf("%d\n", getY(p2)); /* getY is ok */
freep(p1);
freep(p2);
}
/src/point.h
typedef struct _Point Point;
Point *init(int, int);
int getX(Point *);
void setX(Point *, int);
int getY(Point *);
void freep(Point *);
/src/point.c
#include <stdlib.h>
#include "point.h"
struct _Point{
int x;
int y;
};
Point *init(int x, int y)
{
Point *temp;
temp = malloc(sizeof(Point));
temp->x = x;
temp->y = y;
return temp;
}
int getX(Point *p)
{
return p->x;
}
void setX(Point *p, int x)
{
p->x = x;
}
int getY(Point *p)
{
return p->y;
}
void freep(Point *p)
{
free(p);
}
Furthermore, if we need a private method in our class, we do not provide a declaration of it in the header and also we use static to restrict its access within the class's file.
I am a young student who learned C two years ago. I just discovered more complex things like object oriented C.
My first question is:
1) How do you access your variables? The goal would be to have specific values for those variables depending on the struct which has been instantiated.
So I'd like something like:
myStruct* myStrPtr;
myStruct2* myStrPtr;
myStrPtr = initializeStruct();
myStrPtr->printContent //prints for example 55
myStrPtr2 = initializeStruct();
myStrPtr2->printContent //prints for example 6548
example
typedef struct {
void (*sum)(int a, int b);
void (*printContent)(void);
int content;
}myStruct;
void printInhoud(void){
printf("content: %d\n", content);}
void sum(int a, int b){
/***********THIS DOESN T WORK OBVIOUSLY************/
this->content = a+b;
printf("calculated sum: %d", sum);
}
myStruct * initializeStruct(void)
{
myStruct* myStrPtr = malloc(sizeof(myStruct));
myStrPtr -> foo = foo ;
mynStrPtr->printContent = printContent;
return myStrPtr;
}
void freeMem(myStruct * myStructPtr)
{
free(myStructPtr);
}
int main (void)
{
int a= 1;
int b=33;
myStruct* myStrPtr;
myStrPtr = initializeStruct();
myStrPtr->printContent();
return 0;
}
2) my second question is: what are pro's and con's about programming in this way? I think that if I am able to add the variables in the structures and access them just like in OOP, I get a big advantage: modularity. I am active in embedded software and believe that having such an advantage of OOP on an embedded system can be very interesting. I have been criticized for trying to do this. The only reason I was given: "You have no garbage collector, so don't". Could someone give me some pro's and con's and explain why this would be "such an incredibly bad programming practice"
You can do OOP in C, but one of the major cons is that you have to pass the this pointer around. Let's say you have a pointer to a printInfo() function in your struct and you want to print the info of that particular struct. You have to do
my_struct->printInfo(my_struct);
or, if my_struct is not a pointer to your struct
my_struct.printInfo(&my_struct);
There's no way around it.
For your second question, I'm not sure doing OOP in C is really practical. I have done it out of curiosity and it's really fun. You can get inheritance, virtual base classes, polymorphism and all. If you're interested you can check it out here:
https://github.com/vladcc/object-oriented-c
Disclaimer: I'm not sure that the following is truely an answer, but it is way too long for a comment.
Is it possible to do OOP in C?
Yes it is. First C++ compilers were mere pre-processors that converted C++ source to C. Of course, you have neither contructors not destructors so you must explicitely call them, you must use composition pattern for inheritance, you must have vtables for virtual methods and must explicitely pass the this pointer.
Simple example with ctor, dtor, a dynamic array and a method:
struct __foo {
int *arr;
int n;
};
typedef struct __foo foo;
bool init_foo(foo *f, int n) {
f->arr = malloc(n * sizeof(int));
f->n = n;
return (f->arr != NULL);
}
void clean_foo(foo *f) { free(f->arr); }
bool set(int index, int value, foo *f) { // same for get...
if ((index >= f->n) || (index < 0)) return false;
f->arr[index] = value;
return true;
}
Usage:
foo f;
init_foo(8, &f);
set(5, 2, &f);
clean_foo(&f);
More complex example with inheritance and virtual method :
typedef struct {
int age;
const char *name;
const char* (*say)(void *);
} animal;
typedef struct {
animal parent;
} dog;
typedef struct {
animal parent;
} cat;
void init_animal(int age, const char *name, animal *a) {
a->age = age;
a->name = name;
}
char *pre_say(animal *this) {
char * msg = malloc(strlen(this->name) + 11);
strcpy(msg, this->name);
strcat(msg, " says ");
return msg;
const char * wow(void *an) {
animal *this = (animal *) an;
char * msg = pre_say(this);
strcat(msg, "Woof");
return msg;
}
const char* meeow(void *an) {
animal *this = (animal *) an;
char * msg = pre_say(this);
strcat(msg, "Meew");
return msg;
}
void init_dog(int age, const char * name, dog *d) {
init_animal(age, name, &(d->parent));
d->say = &woof;
}
void init_cat(int age, const char * name, cat *c) {
init_animal(age, name, &(c->parent));
d->say = &meeow;
}
Usage example:
dog d;
init_dog(2, "Rintintin", &d);
cat c;
init_cat(3, "Tom", &c);
const char *msg = (d.say)(&d); // msg <- Rintintin says Woof
free(msg);
msg = (c.say)(&c); // msg <- Tom says Meew
free(msg);
Should we do OOP in C?
Definitely NO. As you can say from previous example, as soon as you need polymorphism, pointers must be void * and you loose all possible type checking. And you also loose all the C++ goodies of automatic construtors and destructors, automatic this pointer, etc. You end with much more code, much harder to read and debug. The only acceptable use cases (IMHO) are:
interfacing C++ and C
minimal conversion of C++ code for a platform where you have only a C compiler.
low level codes (kernel or high performance libraries) in which you do not want to be bored with the overhead of C++, and still need OOP.
And just remember: if C++ was invented, it must have been to fill a gap in C language. So don't try to do what C++ was created for in C, unless you really know why you do that.
Has Garbage Collector something to do with OOP?
Here again no. C++ has no garbage collection and is indeed an OO language.
Not only encapsulation, many other OOP concepts can be implemented using C with little bit effort.
Here is an example.
//
// cobj.h
//
#ifndef __COBJ_H__
#define __COBJ_H__
struct _cobj_priv;
typedef struct _cobj {
struct _cobj_priv *priv;
void (*set_data)(struct _cobj *obj, int data);
int (*get_data)(struct _cobj *obj);
void (*print_data)(struct _cobj *obj);
} cobj_t;
cobj_t *new_struct(void);
void free_struct(cobj_t *obj);
#endif /* __COBJ_H__ */
//
// cobj.c
//
#include "cobj.h"
#include <stdio.h>
#include <stdlib.h>
//
// Internal section
//
struct _cobj_priv {
int data;
};
static void set_data (struct _cobj *obj, int data) {
struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;
if (this) {
this->data = data;
}
}
static int get_data (struct _cobj *obj) {
struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;
return (this)? this->data : 0;
}
static void print_data (struct _cobj *obj) {
struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;
if (this)
printf("%d\n", this->data);
}
//
// APIs section
//
cobj_t *new_struct(void) {
cobj_t *obj = malloc(sizeof(cobj_t));
if (obj) {
obj->priv = malloc(sizeof(struct _cobj_priv));
if (obj->priv) {
obj->priv->data = 0;
}
obj->set_data = &set_data;
obj->get_data = &get_data;
obj->print_data = &print_data;
}
return obj;
}
void free_struct(cobj_t *obj) {
if (obj) {
if (obj->priv)
free(obj->priv);
free(obj);
obj = null;
}
}
//
// main.c
//
#include "cobj.h"
#include <stdio.h>
int main(int argc, char *argv[]) {
cobj_t *obj = new_struct();
if (obj) {
obj->print_data(obj);
obj->set_data(obj, 100);
obj->print_data(obj);
printf("> get data return %d\n", obj->get_data(obj));
}
return 0;
}
Result:
0
100
> get data return 100
In C, struct's methods are function pointers, they do not know about the existence of struct so that they can not access to the struct members. You need to pass the struct instance to methods as a parameter in order to access to its members.
What I try to understand the following:
'Union' acts like like a variable and I can store value in it of different types.
What if in it stored value of type 'float', but I read it as 'short'?
Is there any ways how I can be sure about type of value I am going to retrieve from union ?
How can I deal with kind of cases ?
I saw a note somewhere, He talked about using 'enum' for cases like this, but there was no good enough explanation of why ?
Could you explain why is it useful/safe using 'unions' with 'enums', please ? Or show some examples.
Thanks in advance, Nick.
I believe it is a way of implementing tagged unions or sum types.
E.g. in C99 using an anonymous union
enum kind_en { knothing, kint, kfloat, kstring };
struct value_st {
enum kind_en kind;
union {
int n; // when kint
float f; // when kfloat
char* s; // when kstring
};
};
then e.g.
void print_value (struct value_st* v) {
if (!v) {puts("nil"); return; };
switch (v->kind) {
case knothing: puts("nothing"); return;
case kint: printf("int#%d", v->n); return;
case kfloat: printf("float#%g", v->f); return;
case kstring: printf("string'%s'", v->s); return;
default: abort();
}
}
struct value_st* make_int_value(int i) {
struct value_st* val = malloc(sizeof(struct value_st));
if (!val) { perror("malloc int value"); exit(EXIT_FAILURE); };
val->kind = kint;
val->n = i;
return val;
}
A much older example from the previous century is the XEvent type of Xlib
Notice that some programming languages have an easier way of supporting sum types. In Ocaml you need just
type val_t =
Knothing | Kint of int | Kfloat of float | Kstring of string;;
and most importantly you have pattern matching
Answers to your questions:
Yes, encode the type into a struct that contains the union:
union {
float f;
int i;
} my_union;
enum {
its_a_float,
its_an_int
} flavor;
struct {
flavor x;
my_union u;
} data_blob;
Not sure I grok your question, what sort of cases?
see above
It's useful for when you don't know the exact data you have/need at compile time and need to handle multiple flavors of the same logical data.
Add new filed of type enum and keep there information about current type
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
typedef enum types_tag {
CHAR,
INT,
FLOAT
} types_t;
typedef union value_tag {
char c;
int i;
float f;
} value_t;
typedef struct store_tag {
types_t type;
value_t value;
} store_t;
void printValue(const store_t *o) {
switch (o->type) {
case CHAR:
printf("%c\n", o->value.c);
break;
case INT:
printf("%d\n", o->value.i);
break;
case FLOAT:
printf("%.3f", o->value.f);
break;
default:
exit(EXIT_FAILURE);
}
return;
}
void main() {
store_t a;
a.type = CHAR;
a.value.c = 'A';
printValue(&a);
a.type = FLOAT;
a.value.f = 10.45;
printValue(&a);
_getch();
}
Beside that, you can keep information just in bunch of memory and use void*
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
typedef enum types_tag {
CHAR,
INT,
FLOAT
} types_t;
typedef struct store_tag {
types_t type;
void* value;
} store_t;
void printValue(const store_t *o) {
switch (o->type) {
case CHAR:
printf("%c\n", *(char*)(o->value));
break;
case INT:
printf("%d\n", *(int*)(o->value));
break;
case FLOAT:
printf("%.3f", *(float*)(o->value));
break;
default:
exit(EXIT_FAILURE);
}
return;
}
void main() {
store_t a;
a.type = CHAR;
a.value = malloc(1);
*((char*) a.value) = 'A';
printValue(&a);
free(a.value);
a.type = FLOAT;
a.value = malloc(sizeof(float));
*((float*) a.value) = 34.7;
printValue(&a);
free(a.value);
_getch();
}
and add some functions to hide creating and deleting variables.
I have implemented a linked list in C (not C++) that stores pointers to data. I would like to have multiple declarations for its functions (to provide type safety), but have each of them link to the same definition (because there is no actual difference between pointers to different data types, so using the same code reduces space).
Does anyone have any ideas on how to achieve this (or any better ways to do it)? A portable solution is obviously best, but I really just need something that works in GCC.
I believe you might be able to achieve this using typedefs for function prototypes and
casting the generic solution (which deals in void*s) to the specific prototype. This should be safe for compilation because all pointers would be the same size.
Consider this example:
do_something.h:
typedef void (*do_something_with_int_t)(int *i);
extern do_something_with_int_t do_something_with_int;
typedef void (*do_something_with_string_t)(char *s);
extern do_something_with_string_t do_something_with_string;
do_something.c
#include "do_something.h"
void do_something_generic(void* p) {
// Do something generic with p
}
do_something_with_int_t do_something_with_int =
(do_something_with_int_t)do_something_generic;
do_something_with_string_t do_something_with_string =
(do_something_with_string_t)do_something_generic;
As long as do_something_generic is truly datatype-agnostic (i.e. it really doesn't matter what p points to) then this would be OK.
If it's C (not C++), then the following will work just fine. You can adapt the concept to your needs.
tt.h
typedef struct {
int ii;
} Type_1;
typedef struct {
int ii;
} Type_2;
int foo_1(Type_1* ptr) __attribute__((alias("foo")));
int foo_2(Type_2* ptr) __attribute__((alias("foo")));
tt.c
#include <stdio.h>
#include "tt.h"
int main() {
Type_1 t_1;
Type_2 t_2;
foo_1(&t_1);
foo_2(&t_2);
}
int foo(void* arg) {
printf("foo: %p\n", arg);
}
#include <stdio.h>
struct common_type {
int type;
};
struct type1 {
int type;
int value;
};
struct type2 {
int type;
char* p;
};
int func(void *para) {
switch (((struct common_type*)para)->type) {
case 1:
printf("type1,value:%d\n",((struct type1*)para)->value);
break;
case 2:
printf("type2,content:%s\n",((struct type2*)para)->p);
break;
}
}
int main() {
char *s = "word";
struct type1 t1 = {1,1};
struct type2 t2;
t2.type = 2;
t2.p = s;
func((void*)&t1);
func((void*)&t2);
}
I want to learn more about using function pointers in C structs as a way to emulate objects-oriented programming, but in my search, I've just found questions like this where the answer is simply to use a function pointer without describing how that would work.
My best guess is something like this
#include <stdio.h>
#include <stdlib.h>
struct my_struct
{
int data;
struct my_struct* (*set_data) (int);
};
struct my_struct* my_struct_set_data(struct my_struct* m, int new_data)
{
m->data = new_data;
return m;
}
struct my_struct* my_struct_create() {
struct my_struct* result = malloc((sizeof(struct my_struct)));
result->data = 0;
result->set_data = my_struct_set_data;
return result;
}
int main(int argc, const char* argv[])
{
struct my_struct* thing = my_struct_create();
thing->set_data(1);
printf("%d\n", thing->data);
free(thing);
return 0;
}
But that give me compiler warnings warning: assignment from incompatible pointer type, so obviously I'm doing something wrong. Could someone please provide a small but complete example of how to use a function pointer in a C struct correctly?
My class taught in C does not even mention these. It makes me wonder whether these are actually used by C programmers. What are the advantages and disadvantages of using function pointers in C structs?
The answer given by Andy Stow Away fixes my compiler warning, but doesn't answer my second question. The comments to that answer given by eddieantonio and Niklas R answer my second question, but don't fix my compiler warning. So I'm pooling them together into one answer.
C is not object-oriented and attempting to emulate object-oriented design in C usually results in bad style. Duplicating methods called on structs so that they can be called using a pointer to the struct as I have in my example is no exception. (And frankly, it violates DRY.) Function pointers in structs are more useful for polymorphism. For example, if I had a struct vector that represented a generic container for a linear sequence of elements, it might be useful to store a comparison_func member that was a function pointer to allow sorting and searching through the vector. Each instance of the vector could use a different comparison function. However, in the case of a function that operates on the struct itself, it is better style to have a single separate function that is not duplicated in the struct.
This makes the answer to what is correct more complicated. Is what is correct how to make my above example compile? Is it how to reformat my above example so that it has good style? Or is it what is an example of a struct that uses a function pointer the way C programmer would do it? In formulating my question, I did not anticipate the answer being that my question was wrong. For completeness, I will provide an example of each answer to the question.
Fixing the Compiler Warning
#include <stdio.h>
#include <stdlib.h>
struct my_struct
{
int data;
struct my_struct* (*set_data) (struct my_struct*, int);
};
struct my_struct* my_struct_set_data(struct my_struct* m, int new_data)
{
m->data = new_data;
return m;
}
struct my_struct* my_struct_create()
{
struct my_struct* result = malloc((sizeof(struct my_struct)));
result->data = 0;
result->set_data = my_struct_set_data;
return result;
}
int main(int argc, const char* argv[])
{
struct my_struct* thing = my_struct_create();
thing->set_data(thing, 1);
printf("%d\n", thing->data);
free(thing);
return 0;
}
Reformatting the Style
#include <stdio.h>
#include <stdlib.h>
struct my_struct
{
int data;
};
void my_struct_set_data(struct my_struct* m, int new_data)
{
m->data = new_data;
}
struct my_struct* my_struct_create()
{
struct my_struct* result = malloc((sizeof(struct my_struct)));
result->data = 0;
return result;
}
int main(int argc, const char* argv[])
{
struct my_struct* thing = my_struct_create();
my_struct_set_data(thing, 1);
printf("%d\n", thing->data);
free(thing);
return 0;
}
Demonstrating a Use for Function Pointer in Structs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct my_struct
{
void* data;
int (*compare_func)(const void*, const void*);
};
int my_struct_compare_to_data(struct my_struct* m, const void* comparable)
{
return m->compare_func(m->data, comparable);
}
struct my_struct* my_struct_create(void* initial_data,
int (*compare_func)(const void*, const void*))
{
struct my_struct* result = malloc((sizeof(struct my_struct)));
result->data = initial_data;
result->compare_func = compare_func;
return result;
}
int int_compare(const void* a_pointer, const void* b_pointer)
{
return *(int*)a_pointer - *(int*) b_pointer;
}
int string_compare(const void* a_pointer, const void* b_pointer)
{
return strcmp(*(char**)a_pointer, *(char**)b_pointer);
}
int main(int argc, const char* argv[])
{
int int_data = 42;
struct my_struct* int_comparator =
my_struct_create(&int_data, int_compare);
char* string_data = "Hello world";
struct my_struct* string_comparator =
my_struct_create(&string_data, string_compare);
int int_comparable = 42;
if (my_struct_compare_to_data(int_comparator, &int_comparable) == 0)
{
printf("The two ints are equal.\n");
}
char* string_comparable = "Goodbye world";
if (my_struct_compare_to_data(string_comparator,
&string_comparable) > 0)
{
printf("The first string comes after the second.\n");
}
free(int_comparator);
free(string_comparator);
return 0;
}
In your struct definition, change it to
struct my_struct
{
int data;
struct my_struct* (*set_data) (struct my_struct*,int);
};
and now use the above function pointer in main as
thing->set_data(thing,1);