How do I hide some fields of struct in C? - c

I'm trying to implement a struct person, and I need to hide some fields or make them constant.
A trick for create private fields.
Header:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
} Person;
const char const *getName (Person *p);
int getId (Person *p);
/// OTHER FUNCTIONS
Source
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
/// FUNCTIONS
GCC says that person.c:7:8: error: redefinition a 'struct _person' struct _person
I can write this in a header, but after it, I can't use fields of a struct.
typedef struct _person Person;

A struct cannot have multiple conflicting definitions. As such, you can't create a struct that hides some of the fields.
What you can do however it declare that the struct exists in the header without defining it. Then the caller is restricted to using only a pointer to the struct and using functions in your implementation to modify it.
For example, you could define your header as follows:
typedef struct _person Person;
Person *init(const char *name, int id, float wage, int groupid);
const char *getName (const Person *p);
int getId (const Person *p);
float getWage (const Person *p);
int getGroupid (const Person *p);
And your implementation would contain:
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
Person *init(const char *name, int id, float wage, int groupid)
{
Person *p = malloc(sizeof *p);
strcpy(p->name, name);
p->id = id;
p->wage= wage;
p->groupid= groupid;
return p;
}
...

C has no mechanism for hiding individual members of a structure type. However, by operating only in terms of pointers to such a type, and not providing a definition, you can make the whole type opaque. Users would then have to use the functions you provide to manipulate instances in any way. This is a thing that is sometimes done.
To some extent, you may be able to achieve something like what you describe with a hidden context. For example, consider this:
header.h
typedef struct _person {
float wage;
int groupid;
} Person;
implementation.c
struct _person_real {
Person person; // must be first, and is a structure, not a pointer.
int id;
char name[NAME_MAX_LEN];
};
Now you can do this:
Person *create_person(char name[]) {
struct _person_real *pr = malloc(sizeof(*pr));
if (pr) {
pr->person.wage = DEFAULT_WAGE;
pr->person.groupid = DEFAULT_GROUPID;
pr->id = generate_id();
strncpy(pr->name, name, sizeof(pr->name));
pr->name[sizeof(pr->name) - 1] = '\0';
return &pr->person; // <-- NOTE WELL
} else {
return NULL;
}
}
A pointer to the first member of a structure always points also to the whole structure, too, so if the client passes a pointer obtained from that function back to you, you can
struct _person_real *pr = (struct _person_real *) Person_pointer;
and work on the members from the larger context.
Be well aware, however, that such a scheme is risky. Nothing prevents a user from creating a Person without the larger context, and passing a pointer to it to a function that expects the context object to be present. There are other issues.
Overall, C APIs generally either take the opaque structure approach or just carefully document what clients are permitted to do with the data they have access to, or even just document how everything works, so that users can make their own choices. These, especially the latter, are well aligned with overall C approaches and idioms -- C does not hold your hand, or protect you from doing harm. It trusts you to know what you're doing, and to do only what you intend to do.

You can use a mixin style; e.g. write in the header:
struct person {
float wage;
int groupid;
};
struct person *person_new(void);
char const *getName (struct person const *p);
int getId (struct person const *p);
and in the source
struct person_impl {
struct person p;
char name[NAME_MAX_LEN];
int id;
}
struct person *person_new(void)
{
struct person_impl *p;
p = malloc(sizeof *p);
...
return &p->p;
}
chra const *getName(struct person const *p_)
{
struct person_impl *p =
container_of(p_, struct person_impl, p);
return p->name;
}
See e.g. https://en.wikipedia.org/wiki/Offsetof for details of container_of().

Addendum to John Bollinger's answer:
Although, IMHO, opaque pointer types with accessor functions (init/get/set/destroy) are the most secure approach, there's another option that allows users to place objects on the stack.
It's possible to allocate a single "typeless" chunk of memory as part of the struct and use that memory explicitly (bit by bit / byte by byte) instead of using additional types.
i.e.:
// public
typedef struct {
float wage;
int groupid;
/* explanation: 1 for ID and NAME_MAX_LEN + 1 bytes for name... */
unsigned long private__[1 + ((NAME_MAX_LEN + 1 + (sizeof(long) - 1)) / sizeof(long))];
} person_s;
// in .c file (private)
#define PERSON_ID(p) ((p)->private__[0])
#define PERSON_NAME(p) ((char*)((p)->private__ + 1))
This is a very strong indicator that access to the data in the private__ member should be avoided. Developers that don't have access to the implementation file won't even know what's in there.
Having said that, the best approach is an opaque type, as you may have encountered when using the pthread_t API (POSIX).
typedef struct person_s person_s;
person_s * person_new(const char * name, size_t len);
const char * person_name(const person_s * person);
float person_wage_get(const person_s * person);
void person_wage_set(person_s * person, float wage);
// ...
void person_free(person_s * person);
Notes:
avoid typedef with a pointer. It only confuses developers.
It's better to keep pointers explicit, so all developers can know that the type they're using is dynamically allocated.
EDIT: Also, by avoiding "typedefing" a pointer type, the API promises that future / alternative implementations will also use a pointer in it's API, allowing developers to trust and rely on this behavior (see comments).
When using an opaque type, the NAME_MAX_LEN could be avoided, allowing names of arbitrary length (assuming renaming requires a new object). This is an extra incentive to prefer the opaque pointer approach.
avoid placing the _ at the beginning of an identifier when possible (i.e., _name). Names starting with _ are assumed to have a special meaning and some are reserved. The same goes for types ending with _t (reserved by POSIX).
Notice how I use the _s to mark the type as a struct, I don't use _t (which is reserved).
C is more often snake_case (at least historically). The best known APIs and most of the C standard is snake_case (except where things were imported from C++).
Also, being consistent is better. Using CamelCase (or smallCamelCase) in some cases while using snake_case for other things could be confusing when developers try to memorize your API.

What John Bollinger wrote is a neat way of utilising how structs and memory works, but it's also an easy way to get a segfault (imagine allocating an array of Person and then later passing the last element to a 'method' which accesses the id or it's name), or corrupt your data (in an array of Person the next Person is overwriting 'private' variables of the previous Person). You'd have to remember that you must create an array of pointers to Person instead of array of Person (sounds pretty obvious until you decide to optimise something and think that you can allocate and initialise the struct more efficiently than the initialiser function).
Don't get me wrong, it's a great way to solve the problem, but you've got to be careful when using it.
What I'd suggest (though using 4/8 bytes more memory per Person) is to create a struct Person which has a pointer to another struct which is only defined in the .c file and holds the private data. That way it'd be harder to make a mistake somewhere (and if it's a bigger project then trust me - you'll do it sooner or later).
.h file:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
_personPriv *const priv;
} Person;
void personInit(Person *p, const char *name);
Person* personNew(const char *name);
const char const *getName (Person *p);
int getId (Person *p);
.c file:
typedef struct {
int id;
char name[NAME_MAX_LEN];
} _personPriv;
const char const *getName (Person *p) {
return p->priv->name;
}
int getId (Person *p) {
return p->priv->id;
}
_personPriv* _personPrivNew(const char *name) {
_personPriv *ret = memcpy(
malloc(sizeof(*ret->priv)),
&(_personPriv) {
.id = generateId();
},
sizeof(*ret->priv)
);
// if(strlen(name) >= NAME_MAX_LEN) {
// raise an error or something?
// return NULL;
// }
strncpy(ret->name, name, strlen(name));
return ret;
}
void personInit(Person *p, const char *name) {
if(p == NULL)
return;
p->priv = memcpy(
malloc(sizeof(*p->priv)),
&(_personPriv) {
.id = generateId();
},
sizeof(*p->priv)
);
ret->priv = _personPrivNew(name);
if(ret->priv == NULL) {
// raise an error or something
}
}
Person* personNew(const char *name) {
Person *ret = malloc(sizeof(*ret));
ret->priv = _personPrivNew(name);
if(ret->priv == NULL) {
free(ret);
return NULL;
}
return ret;
}
Side note: this version can be implemented so that private block is allocated right after/before the 'public' part of the struct to improve locality. Just allocate sizeof(Person) + sizeof(_personPriv) and initialise one part as Person and second one as _personPriv.

Related

passing array of variables to function in C

I have a C function which needs a large amount of variables to be passed, so I came to the idea of "packing" them all in a single array (matrix of variables). The point is, these variables are of a very different type, some int, some arrays (strings and vectors), and many of them float. Is there a way to leave unspecified the type of data stored into the matrix? (I unsuccessfully explored the void "data type")
The elements of an array are always of a single type, that's the point.
Collecting variables of multiple types is the job for a structure, i.e. a struct.
This is a quite common way to solve this particular problem. If the structure becomes large, you might find it convenient to pass a pointer to an instance of it, rather than copying the entire thing in the call.
You can use va_list but struct is the best way to do it
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
enum type {
INT,
FLOAT,
PCHAR,
};
struct any_type {
enum type type_;
union {
int int_;
float float_;
char* pchar_;
};
};
#define MYSIZE 10
void process(size_t size, struct any_type* array)
{
for(int i = 0; i < size; i++) {
switch(array[i].type_) {
case INT :
printf("INT: %d\n", array[i].int_);
break;
case FLOAT :
printf("FLOAT: %f\n", array[i].float_);
break;
case PCHAR :
printf("PCHAR: %s\n", array[i].pchar_);
break;
default:
printf("UNKNOWN TYPE PROVIDED\n");
break;
}
}
}
int main(int argc, char *argv[])
{
struct any_type *array;
array = malloc(MYSIZE*(sizeof(struct any_type)));
array[0].type_ = INT;
array[0].int_ = 10;
array[1].type_ = FLOAT;
array[1].float_ = 2.5;
array[2].type_ = PCHAR;
array[2].pchar_ = "hello char";
process(3, array);
return 0;
}
You can extend type and union as needed. However using nameless unions require -std=c11.
Expanding on my comment above:
Needing to pass a large number of parameters1 to a function can be a sign that there is a problem in your design - your function may be trying to do too many things at once, and you would be better off refactoring it into several smaller functions, each of which only takes a subset of the parameters.
Assuming that's not the case, how are your parameters logically related to each other? Can they be considered attributes of a single data item? For example, a person may be described by the following attributes: surname, given name, birth date, sex. These can be collected together into a single struct type such as
#include <time.h>
struct person {
char *surname;
char *name;
struct tm birthdate; // struct tm defined in time.h
char sex;
};
void write_to( struct person *p )
{
p->surname = strdup( "McGillicuddy" );
p->name = strdup( "Aloysius" );
p->sex = 'M';
p->birthdate.tm_year = 32; // tm_year starts at 1900, so this is 1932
p->birthdate.tm_mon = 11; // december
p->birthdate.tm_day = 1;
};
int main( void )
{
struct person p;
...
write_to( &p );
...
}
Note that members of struct types can themselves be struct types - struct tm is a type defined in time.h that specifies a datetime value using multiple attributes.
Some notes on syntax:
When you want to access a member of a struct instance, use the . operator. When you want to access a member of a struct through a pointer, use the -> operator. In the function write_to, p is a pointer to struct person, so to access each member of p we use ->. The birthdate member is an instance of struct tm, not a pointer, so we use the . operator to access each member of birthdate.
p->m is equivalent to (*p).m.
Like I said in my comment, you should not collect otherwise unrelated items into a struct type just to reduce the number of parameters being passed to a function. They should all be attributes of a more complex type. Some other examples of what I mean:
// A node in a list
struct node {
data_t data; // for some data type data_t;
struct node *next;
struct node *prev;
};
// A street address
struct addr {
char *number; // to handle things like 102A, 102B
char *street;
char *city;
char state[3];
char *zip;
};
It's possible that you're really passing only a couple of distinct data items to your function, each of which is composed of a lot of different attributes. Take a step back and look at your variables and see how they relate to each other.
"Large" depends on context, and of course there are always exceptions to any rule, but in general passing more than 7 distinct, unrelated parameters is a sign you may need to refactor your function into several smaller functions.

C - How to manipulate typedef structure pointer?

typedef struct
{
int id;
char* first;
char* last;
}* person;
person* people;
Hi.
How can I use this above, all set globally, to fill people with different "person"s? I am having issues wrapping my head regarding the typedef struct pointer.
I am aware pointers are like arrays, but I'm having issues getting this all together...
I would like to keep the above code as is as well.
Edit 1: char first should be char* first.
Ugly as sin. You really should redefine person to not be a pointer. Also don't use anonymous structs.
#include <stdio.h>
typedef struct {
int id;
char* first;
char* last;
}* person;
person* people = (person[]){
(person)&(struct {int id;char* first;char* last;}){0,"me","foo"},
(person)&(struct {int id;char* first;char* last;}){0,"you","foo"},
NULL
};
int main(void) {
while(*people) {
printf("%s %s\n", (*people)->first, (*people)->last);
people++;
}
return 0;
}
Don't bother with typedefs for structs. It's much clearer if you use structs with tags and then do your thing:
struct PERSON {
int id;
char *first;
char *last;
};
struct PERSON *people; /* people is a pointer to a struct PERSON. */
/* Allocate array of 42 struct PERSONS. */
people = malloc (42 * sizeof *people);
/* Now use people[0] to people[41]. */
You could simply create an array of structures of type person using something like following:
people = malloc (num_person * sizeof(person));
for (i = 0; i < num_person; i++) {
people[i]->first = malloc (size * sizeof(char));
people[i]->last = malloc (size * sizeof(char));
}
Following this, you could fill up each people struct with different parameters.
Typedefs that hide the real type are a bad idea.
If you really want the double indirection, I guess something like:
people = malloc(sizeof(person));
*people = malloc(SOME_NUMBER * sizeof(**people));
Will allocate what you need. You'd get an individual person structure out like:
(*people)[INDEX]
When using a particular person structure, you'll need to allocate memory for the first and last strings as well:
(*people)[INDEX].first = malloc(STRING_SIZE);
(*people)[INDEX].last = malloc(STRING_SIZE);
But simply removing the crazy double indirection will really clean things up and make everything a lot easier to use and understand:
people = malloc(SOME_NUMBER * sizeof(person));
people[INDEX].first = malloc(STRING_SIZE);
people[INDEX].last = malloc(STRING_SIZE);

How to work with string fields in a C struct?

I'm having trouble making a database based on a singly-linked list in C,
not because of the linked list concept but rather the string fields in the struct themselves.
This is an assignment in C and as far as I know (I'm a newbie), C doesn't recognize 'string' as a data type.
This is what my struct code looks like:
typedef struct
{
int number;
string name;
string address;
string birthdate;
char gender;
} patient;
typedef struct llist
{
patient num;
struct llist *next;
} list;
I was thinking of making a struct for the strings themselves so that I can use them in the struct, like this:
typedef struct string
{
char *text;
} *string;
Then I will malloc() each one of them when it is required to make new data of the string type (array of char).
typedef struct string
{
char *text;
} *string;
int main()
{
int length = 50;
string s = (string) malloc(sizeof string);
s->text = (char *) malloc(len * sizeof char);
strcpy(s->text, patient.name->text);
}
Can someone help me figure this out?
Thank you.
On strings and memory allocation:
A string in C is just a sequence of chars, so you can use char * or a char array wherever you want to use a string data type:
typedef struct {
int number;
char *name;
char *address;
char *birthdate;
char gender;
} patient;
Then you need to allocate memory for the structure itself, and for each of the strings:
patient *createPatient(int number, char *name,
char *addr, char *bd, char sex) {
// Allocate memory for the pointers themselves and other elements
// in the struct.
patient *p = malloc(sizeof(struct patient));
p->number = number; // Scalars (int, char, etc) can simply be copied
// Must allocate memory for contents of pointers. Here, strdup()
// creates a new copy of name. Another option:
// p->name = malloc(strlen(name)+1);
// strcpy(p->name, name);
p->name = strdup(name);
p->address = strdup(addr);
p->birthdate = strdup(bd);
p->gender = sex;
return p;
}
If you'll only need a few patients, you can avoid the memory management at the expense of allocating more memory than you really need:
typedef struct {
int number;
char name[50]; // Declaring an array will allocate the specified
char address[200]; // amount of memory when the struct is created,
char birthdate[50]; // but pre-determines the max length and may
char gender; // allocate more than you need.
} patient;
On linked lists:
In general, the purpose of a linked list is to prove quick access to an ordered collection of elements. If your llist contains an element called num (which presumably contains the patient number), you need an additional data structure to hold the actual patients themselves, and you'll need to look up the patient number every time.
Instead, if you declare
typedef struct llist
{
patient *p;
struct llist *next;
} list;
then each element contains a direct pointer to a patient structure, and you can access the data like this:
patient *getPatient(list *patients, int num) {
list *l = patients;
while (l != NULL) {
if (l->p->num == num) {
return l->p;
}
l = l->next;
}
return NULL;
}
I think this solution uses less code and is easy to understand even for newbie.
For string field in struct, you can use pointer and reassigning the string to that pointer will be straightforward and simpler.
Define definition of struct:
typedef struct {
int number;
char *name;
char *address;
char *birthdate;
char gender;
} Patient;
Initialize variable with type of that struct:
Patient patient;
patient.number = 12345;
patient.address = "123/123 some road Rd.";
patient.birthdate = "2020/12/12";
patient.gender = 'M';
It is that simple. Hope this answer helps many developers.
While Richard's is what you want if you do want to go with a typedef, I'd suggest that it's probably not a particularly good idea in this instance, as you lose sight of it being a pointer, while not gaining anything.
If you were treating it a a counted string, or something with additional functionality, that might be different, but I'd really recommend that in this instance, you just get familiar with the 'standard' C string implementation being a 'char *'...
You could just use an even simpler typedef:
typedef char *string;
Then, your malloc would look like a usual malloc:
string s = malloc(maxStringLength);
This does not work:
string s = (string)malloc(sizeof string);
string refers to a pointer, you need the size of the structure itself:
string s = malloc(sizeof (*string));
Note the lack of cast as well (conversion from void* (malloc's return type) is implicitly performed).
Also, in your main, you have a globally delcared patient, but that is uninitialized. Try:
patient.number = 3;
patient.name = "John";
patient.address = "Baker street";
patient.birthdate = "4/15/2012";
patient.gender = 'M';
before you read-access any of its members
Also, strcpy is inherently unsafe as it does not have boundary checking (will copy until the first '\0' is encountered, writing past allocated memory if the source is too long). Use strncpy instead, where you can at least specify the maximum number of characters copied -- read the documentation to ensure you pass the correct value, it is easy to make an off-by-one error.

typecheck for return value

I have a list in which i want to be able to put different types. I have a function that returns the current value at index:
void *list_index(const List * list, int index) {
assert(index < list->size);
return list->data[index];
}
In the array there are multiple types, for example:
typedef struct structA { List *x; char *y; List *z; } structA;
typedef struct structB { List *u; char *w; } structB;
Now in order to get data from the array:
structA *A;
structB *B;
for(j=0... ) {
A = list_index(list, j);
B = list_index(list, j);
}
But now how do I find out the type of the return value? Is this possible with typeof (I'm using GCC btw)?
And is this even possible or do i have to make some sort of different construction?
You'll have to use unions like shown here.
The best way to solve this would be to use unions.
Another way would be to memcpy() the list item to an actual struct (i.e., not a pointer) of the appropriate type. This would have the advantage of making each List item as small as possible.
A third way would be to just cast the pointer types as in type punning. C allows this as long as the object is dereferenced with its either its correct type or char.
Either way, you will need to put a code in each structure that identifies the type of object. There is no way the compiler can figure out what a pointer points to for you. And even if you could use typeof, you shouldn't. It's not C99.
Technically, if you don't use a union, you will have a problem making a legal C99 access to the type code, because you will need to make a temporary assumption about the type and this will violate the rule that objects must be dereferenced as their actual type, via a union, or via a char *. However, since the type code must by necessity be in the same position in every type (in order to be useful) this common technical violation of the standard will not actually cause an aliasing optimization error in practice.
Actually, if you make the type code a char, make it the first thing in the struct, and access it via a char *, I think you will end up with code that is a bit confusing to read but is perfectly conforming C99.
Here is an example, this passes gcc -Wall -Wextra
#include <stdio.h>
#include <stdlib.h>
struct A {
char typeCode;
int something;
};
struct B {
char typeCode;
double somethingElse;
};
void *getMysteryList();
int main()
{
void **list = getMysteryList();
int i;
for (i = 0; i < 2; ++i)
switch (*(char *) list[i]) {
case 'A':
printf("%d\n", ((struct A *) list[i])->something);
break;
case 'B':
printf("%7.3f\n", ((struct B *) list[i])->somethingElse);
break;
}
return 0;
}
void *getMysteryList()
{
void **v = malloc(sizeof(void *) * 2);
struct A *a = malloc(sizeof(struct A));
struct B *b = malloc(sizeof(struct B));
a->typeCode = 'A';
a->something = 789;
b->typeCode = 'B';
b->somethingElse = 123.456;
v[0] = a;
v[1] = b;
return v;
}
C handles types and typing entirely at compile time (no dynamic typing), so once you've cast a pointer to a 'void *' its lost any information about the original type. You can cast it back to the original type, but you need to know what that is through some other method.
The usual way to do this is with some kind of type tag or descriptor in the beginning of all the objects that might be stored in your list type. eg:
typedef struct structA { int tag; List *x; char *y; List *z; } structA;
typedef struct structB { int tag; List *u; char *w; } structB;
enum tags { structAtype, structBtype };
You need to ensure that every time you create a structA or a structB, you set the tag field properly. Then, you can cast the void * you get back from list_index to an int * and use that to read the tag.
void *elem = list_index(list, index)
switch (*(int *)elem) {
case structAtype:
/* elem is a structA */
:
case structBtype:
/* elem is a structB */
Make the elements you want to put into the list all inherit from a common base class. Then you can have your base class contain members that identify the actual type.
class base {
public:
typedef enum {
type1,
type2,
type3
} realtype;
virtual realtype whatAmI()=0;
};
class type_one : public base {
public:
virtual base::realtype whatAmI() { return base::type1; };
};
class type_two : public base {
public:
virtual base::realtype whatAmI() { return base::type2; };
};
After that, you'd declare your list type like:
std::list<base *> mylist;
and you can stuff pointers to any of the derived types into the list. Then when you take them out, you can just call 'whatAmI()' to find out what to cast it to.
Please note: Trying to do this in C++ means you are doing something in a way that's not a good match for C++. Any time you deliberately evade the C++ type system like this, it means you're giving up most of the usefulness of C++ (static type checking), and generally means you're creating large amounts of work for yourself later on, not only as you debug the first iteration of this app, but especially at maintenance time.
You have some choices. Keep in mind that C is basically not a dynamically typed language.
You Make a common base for the structs, and put a simple type indicator of your own in it.
struct base {
int type_indication:
};
then
struct structA {
struct base base;
...
};
and then you can cast the pointer to (struct base *).

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