Implementing basic vtable in C - c

I'm trying to emulate the most basic (toy) case of a vtable in C. Here is a basic example:
typedef struct Person {
int id;
char *name;
} Person;
And let's say we add in one method (i.e., function pointer):
typedef struct Person {
int id;
char *name;
void (*print_name)(Person);
} Person;
And now we'll initialize it and fill in the pieces with this (let’s ignore memory leaks):
#include <stdio.h>
#include <stdlib.h>
typedef struct Person Person;
typedef struct Person {
int id;
char *name;
void (*print)(Person *self);
} Person;
void print_name(Person *person) {
printf("Hello %s\n", person->name);
}
Person *init_person(void) {
Person *person = malloc(sizeof(Person));
person->print = print_name;
}
int main(void) {
Person *p = init_person();
p->name = "Greg";
p->print(p);
return 0;
}
Running code here.
If I were to factor out the functions from the Person and put it in a Person_VTable, such as the following:
typedef struct Person {
int id;
char *name;
Person_VTable *vtable;
} Person;
typedef struct Person_VTable {
???
} Person_VTable;
What would be the proper way to (1) create the vtable, and (2) initialize the Person object with the new vtable? Note, I know this is an entirely trivial example and it can be done in better ways, but I'm seeing how it can be done with an external 'vtable' to the main object I'm working work.
Also, does this also mean if I have a vtable, instead of just having the one 'self' to reference the object it's coming from when in the struct itself, such as:
void (*print)(Person *self);
I need to have two indirections, so I know both the object and the vtable location? Something like:
void (*print)(Person *self_obj, Person_VTable *self_vt);
If so, that's a lot of overhead!

A basic vtable is nothing more than an ordinary struct containing function pointers, which can be shared between object instances. There are two basic ways one can implement them. One is to make the vtable pointer an ordinary struct member (this is how it works in C++ under the hood):
#include <stdio.h>
#include <stdlib.h>
typedef struct Person Person;
typedef struct Person_VTable Person_VTable;
struct Person {
int id;
char *name;
const Person_VTable *vtable;
};
struct Person_VTable {
void (*print)(Person *self);
};
void print_name(Person *person) {
printf("Hello %s\n", person->name);
}
static const Person_VTable vtable_Person = {
.print = print_name
};
Person *init_person(void) {
Person *person = malloc(sizeof(Person));
person->vtable = &vtable_Person;
return person;
}
int main(void) {
Person *p = init_person();
p->name = "Greg";
p->vtable->print(p);
return 0;
}
Another is to use fat pointers (this is how it’s implemented in Rust):
#include <stdio.h>
#include <stdlib.h>
typedef struct Person Person;
typedef struct Person_VTable Person_VTable;
typedef struct Person_Ptr {
Person *self;
const Person_VTable *vtable;
} Person_Ptr;
struct Person {
int id;
char *name;
const Person_VTable *vtable;
};
struct Person_VTable {
void (*print)(Person_Ptr self);
};
void print_name(Person_Ptr person) {
printf("Hello %s\n", person.self->name);
}
static const Person_VTable vtable_Person = {
.print = print_name
};
Person_Ptr init_person(void) {
Person_Ptr person;
person.self = malloc(sizeof(Person));
person.vtable = &vtable_Person;
return person;
}
int main(void) {
Person_Ptr p = init_person();
p.self->name = "Greg";
p.vtable->print(p);
return 0;
}
In C, the preferred way is the former, but that’s mostly for syntax reasons: passing structs between functions by value doesn’t have a widely-agreed-upon ABI, while passing two separate pointers is rather unwieldy syntactically. The other method is useful when attaching a vtable to an object whose memory layout is not under your control.
In essence, the only advantages of vtables over ordinary function pointer members is that they conserve memory (each instance of the struct only needs to carry one vtable pointer) and protect against memory corruption (the vtables themselves can reside in read-only memory).

You can do this without making too many changes. To start, we will define the Person and Person_VTable types:
typedef struct Person Person;
typedef struct Person_VTable {
void (*print) (Person* self);
} Person_VTable;
typedef struct Person {
int id;
char* name;
Person_VTable *vtable;
} Person;
Here we have replaced the previous Person function pointer with a pointer to the Person_VTable type, which will hold our functions. The Person_VTable itself is almost identical to what the functions in the Person type were previously -- notice even that the function signatures are the same. For example:
// old
typedef struct Person {
// ...
void (*print)(Person *self);
} Person;
// new
typedef struct Person_VTable {
// ...
void (*print)(Person *self);
} Person_VTable;
Our init function is also similar, however now we need to malloc for the Person_VTable object, since that is now held outside our main Person object. This will give us:
Person* init_person(void) {
Person *person = malloc(sizeof(Person));
// malloc the vtable and attach the print method to it.
person->vtable = malloc(sizeof(Person_VTable));
person->vtable->print = print_name;
return person;
}
Finally, to put everything together with a working example here:
#include<stdio.h>
#include<stdlib.h>
typedef struct Person Person;
typedef struct Person_VTable {
void (*print) (Person* self);
} Person_VTable;
typedef struct Person {
int id;
char* name;
Person_VTable *vtable;
} Person;
void print_name(Person* person) {
printf("Hello %s\n", person->name);
}
Person* init_person(void) {
Person *person = malloc(sizeof(Person));
person->vtable = malloc(sizeof(Person_VTable));
person->vtable->print = print_name;
return person;
}
int main(void) {
Person* self = init_person();
self->name = "Greg";
self->vtable->print(self);
return 0;
}

Related

Making two objects in C more object-oriented

First, this is merely an academic question. I know C is not the job for doing OOP programming, but this is more of a learning exercise for a beginner learning what's possible and what's not (or perhaps, what might be possible but is not a good idea).
Let's take the following as a starting place, where I have two different objects but I want to give each of them the same two methods: create and print. I've omitted any error checking, freeing, etc. just to simplify matters:
#include <stdio.h>
#include <stdlib.h>
struct Person {
char* name;
int age;
};
struct Car {
char* make;
char* model;
int year;
};
struct Person* create_person(void)
{
struct Person *new = malloc(sizeof (struct Person));
return new;
}
void print_person(struct Person* person)
{
printf("<Person: %s (%d)>\n", person->name, person->age);
}
struct Car* create_car(void)
{
struct Car *new = malloc(sizeof (struct Car));
return new;
}
void print_car(struct Car* car)
{
printf("<Car: %s - %s (%d)>\n", car->make, car->model, car->year);
}
int main(void)
{
struct Car *car = create_car();
*car = (struct Car) {.make="Chevy", .model="Eldorado", .year=2015};
print_car(car);
struct Person *person = create_person();
*person = (struct Person) {.name="Tom", .age=30};
print_person(person);
}
I would think that the first part would be to group the 'methods' into the struct itself. So then we would have:
#include <stdio.h>
#include <stdlib.h>
struct Person {
char* name;
int age;
void (*print)(struct Person*);
};
struct Car {
char* make;
char* model;
int year;
void (*print)(struct Car*);
};
void print_car(struct Car* car);
void print_person(struct Person* person);
struct Person* create_person(void)
{
struct Person *new = malloc(sizeof (struct Person));
return new;
}
void print_person(struct Person* person)
{
printf("<Person: %s (%d)>\n", person->name, person->age);
}
struct Car* create_car(void)
{
struct Car *new = malloc(sizeof (struct Car));
return new;
}
void print_car(struct Car* car)
{
printf("<Car: %s - %s (%d)>\n", car->make, car->model, car->year);
}
int main(void)
{
struct Car *car = create_car();
*car = (struct Car) {.make="Chevy", .model="Eldorado", .year=2015, .print=print_car};
car->print(car);
struct Person *person = create_person();
*person = (struct Person) {.name="Tom", .age=30, .print=print_person};
person->print(person);
}
What would be the next step in making it "more OOP like"? Perhaps using preprocessor glue and generics? What would be an example of the most OOP-like that the two objects could become? Again, I know this isn't what C is meant for, but it's more a learning experience.
You could use approach applied by the Linux kernel.
The implementation of OOP is based using a composition for inheritance, and embedding interfaces into new classes.
The macro container_of lets easily alternate between a pointer to class and a pointer to one of its members. Usually an embedded object will be an interface of the class. To find more details about container_of macro see my answer to other but related question.
https://stackoverflow.com/a/66429587/4989451
This methodology was used to create a huge complex object oriented software, i.e. Linux kernel.
In the examples from the question, the Car and Person classes use an printing interface that we can call struct Printable. I strongly suggest to produce a fully initialized objects in create_... functions. Let it make a copy of all strings. Moreover, you should add destroy_... methods to release resources allocated by create_....
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#define container_of(ptr, type, member) \
(type*)(void*)((char*)ptr - offsetof(type, member))
struct Printable {
void (*print)(struct Printable*);
};
struct Person {
char* name;
int age;
struct Printable printable;
};
void print_person(struct Printable *printable) {
struct Person *p = container_of(printable, struct Person, printable);
printf("<Person: %s (%d)>\n", p->name, p->age);
}
struct Person *create_person(char *name, int age) {
struct Person *p = malloc(sizeof *p);
p->name = strdup(name);
p->age = age;
p->printable.print = print_person;
return p;
}
struct Car {
char* make;
char* model;
int year;
struct Printable printable;
};
void print_car(struct Printable *printable) {
struct Car *c = container_of(printable, struct Car, printable);
printf("<Car: %s - %s (%d)>\n", c->make, c->model, c->year);
}
struct Car *create_car(char *make, char *model, int year) {
struct Car *c = malloc(sizeof *c);
c->make = strdup(make);
c->model = strdup(model);
c->year = year;
c->printable.print = print_car;
return c;
}
void print(struct Printable *printable) {
printable->print(printable);
}
int main() {
struct Car *car = create_car("Chevy", "Eldorado", 2015);
struct Person *person = create_person("Tom", 30);
print(&car->printable);
print(&person->printable);
return 0;
}
produces output:
<Car: Chevy - Eldorado (2015)>
<Person: Tom (30)>
Note that function print() takes a pointer to the Printable interface. The function does not need to know anything about the original class. No preprocessor is used. All casts are done on "library" side, not on clients side. The library initializes the Printable interface, therefore it cannot be misused.
You can easily add other base classes or interfaces like i.e. RefCounted to solve memory management. The i-face would contain a pointer to destructor and refcount itself. Other examples are intrusive linked lists or binary trees.

Ways to create a recursive data structure in C

Let's say I have some hashmap that can refer to itself, something like:
typedef struct Person {
char* name;
Person* mother;
Person* father;
} Person;
Person *Bob = malloc(sizeof(Person));
bob->name = "Bob";
bob->mother = Kathy;
bob->father = Bill;
What are the suggested ways to get around the error: unknown type name ‘Person’ error?
Person is not defined yet, since the typedef only takes effect after the semicolon concluding it. To refer to the struct from within itself, use struct Person. The following code compiles with no errors on GCC 10.2.0.
typedef struct Person {
char* name;
struct Person* mother;
struct Person* father;
} Person;
int main() {
Person Kathy = { "Kathy", NULL, NULL };
Person Bill = { "Bill", NULL, NULL };
Person Bob = { "Bob", &Kathy, &Bill };
return 0;
}
The problem is that the name Person used within the structure definition as a type specifier for data members mother and father
typedef struct Person {
char* name;
Person* mother;
Person* father;
} Person;
is not yet declared.
Either use a typedef before the structure definition like
typedef struct Person Person;
struct Person{
char* name;
Person* mother;
Person* father;
};
Or use the declared structure tag in the structure definition like
typedef struct Person {
char* name;
struct Person* mother;
struct Person* father;
} Person;
While the typedef is not defined, the struct tag is so you can prepend that to the elements of the struct. For example:
typedef struct Person {
char* name;
struct Person* mother;
struct Person* father;
} Person;
#include <stdlib.h>
int main(void) {
// parents (ignore their parents)
Person *Kathy = malloc(sizeof(Person));
Kathy->name = "Kathy";
Person *Bill = malloc(sizeof(Person));
Bill->name = "Bill";
// person
Person *Bob = malloc(sizeof(Person));
Bob->name = "Bob";
Bob->mother = Kathy;
Bob->father = Bill;
printf("Name: %s | Mom: %s, Dad: %s\n", Bob->name, Bob->mother->name, Bob->father->name
free(Bob); free(Kathy); free(Bill);
}
Name: Bob | Mom: Kathy, Dad: Bill

Declare struct inside main()

I am new to C. I got asked to modify this program so that the variables student and anotherStudent are not global but are local to main.It will still be printed by printStudnets. typedef is not allowed to use.
I understand if declare struct in main and it would be ascessible to the main function only.Do I have to declare struct in each function to achieve this?
#include <stdio.h>
#include <stdlib.h>
struct student_s {
char* name;
int age;
struct student_s* next;
} student;
struct student_s anotherStudent;
void printOneStudent(struct student_s student)
{
printf("%s (%d)\n", student.name, student.age);
}
void printStudents(const struct student_s* student)
{
while (student != NULL) {
printOneStudent(*student);
student = student->next;
}
}
int main(void)
{
student.name = "Agnes McGurkinshaw";
student.age = 97;
student.next = &anotherStudent;
anotherStudent.name = "Jingwu Xiao";
anotherStudent.age = 21;
anotherStudent.next = NULL;
printStudents(&student);
return EXIT_SUCCESS;
}
You don't need to use typedef in order to define a new structured type. This is perfectly valid:
struct student_s {
char* name;
int age;
struct student_s* next;
}; // Remove "student". Now you don't have a global variable.
A consequence of this is that student_s is not a name of your structured type; it is a tag of your structured type. Therefore, declaring objects of structured type corresponding to student_s must start with the keyword struct:
int main(void)
{
struct student_s student;
... // The rest of your code remains the same
}

Calling base function from derived

I took this tutorial example Inheritance and Polymorphism in C and because I've customized it for my exact requirements it's throwing an error when I try to call base function.
Question: Why does it fail in line 8 of employee.c and Possible resolution
((Employee *)self)->super.display(self); // Sementation fault: 11
Download Project
main.c
#include "person.h"
#include "employee.h"
#include <stdio.h>
int main() {
Person* person = newPerson("John Doe");
Employee* employee = newEmployee("Jane Doe", "Acme", 40000);
person->display(person); // displaying Person object
puts("------");
employee->display((Person*)employee); // displaying employee info
return 0;
}
Person.h
#ifndef _PERSON_H
#define _PERSON_H
#include <stdlib.h>
typedef struct Person Person;
struct Person {
char* name;
void (*display)(Person*);
};
Person* newPerson(char* name);
#endif
Person.c
#include "person.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static void display(Person* const self) {
printf("Name: %s\n", self->name);
}
Person* newPerson(char* name) {
Person* person = malloc(sizeof(Person));
person->name = name;
person->display = display;
return person;
}
Employee.h
#include "person.h"
typedef struct Employee Employee;
struct Employee {
Person super;
char* company;
int salary;
void (*display)(Person*);
};
Employee* newEmployee(char* name, char* company, int salary);
Employee.c
#include "employee.h"
#include <string.h>
#include <stdio.h>
static void display(Person* const self) {
puts(((Employee*)self)->super.name); // works
// ((Employee *)self)->super.display(self); // Sementation fault: 11
printf("Company: %s\n", ((Employee *)self)->company);
printf("Salary: %d\n", ((Employee*)self)->salary);
}
Employee* newEmployee(char* name, char* company, int salary) {
Employee* employee = malloc(sizeof(Employee));
employee->super.name = name;
employee->company = company;
employee->salary = salary;
employee->display = display;
return employee;
}
Probably, for every class in the chain, starting with the base Person, you should make method implementations available under separate names:
Person.h
typedef struct _Person Person;
typedef struct _Person {
void* derived;
char* first;
char* last;
void (*display)(Person*);
} Person;
Person* newPerson(char* first, char* last);
void Person_display(Person *); // analogous to Person::display in C++
Person.c
Person* newPerson(char* first, char* last) {
Person* person = (Person*)malloc(sizeof(Person));
person->derived = person; // pointing to itself
person->first = first;
person->last = last;
person->display = Person_display; // Initializing interface for access to functions
return person;
}
Employee.h
void Employee_display(Person const *); // available to lower subclasses
And in Employee.c
static void display(Person* const self) {
Person_display(self); // calling the superclass implementation
Employee *employee = self->derived;
printf("Company: %s\n", employee->company);
printf("Salary: %d\n", employee->salary);
}
Person* newEmployee(char* first, char* last, char* company, int salary) {
Person* person = newPerson(first, last); // calling base class constructor
Employee* employee = malloc(sizeof(Employee));
person->derived = employee; // pointing to derived object
employee->company = company; // initialising derived class members
employee->salary = salary;
person->display = Employee_display; // Changing base class interface to access derived class functions
return person;
}
Note that this is consistent with usual C++ virtual method contracts: calling display() from the base class ctor resolves to the base class's implementation and the derived class's method is only available after the base class subobject has been fully constructed.
The problem was because embedded struct in Employee didn't have display function pointer initialized and pointed to a function
struct Employee {
Person super;
...
}
Solution: Change the embedded structure Person to pointer type and call newPerson for super
employee.h
typedef struct Employee Employee;
struct Employee {
Person *super; // change this pointer type
char* company;
int salary;
void (*display)(Person*);
};
Employee* newEmployee(char* name, char* company, int salary);
employee.c
static void display(Person* const self) {
((Employee*)self)->super->display(((Employee*)self)->super);
printf("Company: %s\n", ((Employee *)self)->company);
printf("Salary: %d\n", ((Employee*)self)->salary);
}
Employee* newEmployee(char* name, char* company, int salary) {
Employee* employee = malloc(sizeof(Employee));
employee->super = newPerson(name); // call constructor here
employee->company = company;
employee->salary = salary;
employee->display = display;
return employee;
}
What you're currently trying to do is this.
First, you define a 'parent' structure:
typedef struct _Person {
void* derived;
char* first;
char* last;
void (*display)(Person*);
} Person;
Next, you define a 'derived' structure:
typedef struct _Employee {
Person* super;
char* company;
int salary;
void (*display)(Person*);
} Employee;
And finally you cast one type to the other:
return (Person*)employee;
which is wrong. It takes memory allocated for the Employee struct and tries to interpret it as Person. In other words, regards super as derived, company as first and bit pattern in salary as last. I hope you realize that's not quite what you meant.
The Person subobject of your Employee is actually pointed-to by super. Of course you can return employee->super from newEmployee(), this will be a correct instance of Person, but this is really a Person, a concrete instance of Person. It's not polymorphic anymore, and the employee-specific part of the object will then be lost, unrecoverable, unreachable and leaked — there's no way to downcast Person to Employee.
You have two options.
Change the declaration of struct _Employee to
typedef struct _Employee {
Person super;
This way you have immediate up- and downcasts (simply cast Employee * to Person *, and vice versa). All the Person properties of an Employee will then be accessible via its super: employee->super.display = display (where the display being assigned is that static procedure defined in Employee.c; to access Employee-specific portion of the object, it needs to downcast it to Person).
The obvious caveat to this approach is some loss of type safety (given a pointer to Person you cannot tell whether it is a Person or an Employee; this can be worked around by explicitly defining a concrete type descriptor in the base class:
struct _Person {
enum { PERSON, EMPLOYEE, STRANGER, UNDERCOVER_AGENT } concreteClass;
Now you have a run-time type information, but you have limited the allowed set of subclasses to your Person which is not how polymorphic types are commonly implemented.)
Stick to the original design with void *derived pointing to concrete subclass-specific part of the object.
As a side note, your original idea with derived pointing to the struct itself in case it is an instance of Person is a rather elegant way to distinguish instantiable base classes from those abstract :) : by convention, abstract class's constructor sets derived to NULL and leaves it to the derived class's ctor to set that to proper value; and each virtual method first checks whether it is non-NULL and throws an exception otherwise.

struct has a function pointer to a function that takes a pointer of said struct .. need to predfine?

I'm trying to create a structure, that has a function pointer. That function pointer points to a function, that takes a pointer of said structure. This is a real chicken-or-the-egg problem because the prototype needs to know about the structure and the structure needs to know about the prototype. Is there a way to predefine the struct? I'm new to C so if anyone has any insight I would greatly appreciate it.
Thanks,
-devnull
#include <stdio.h>
/* predefine struct person? */
void printInfo(struct person *);
struct person{
char *name;
int age;
const void *pf = printInfo;
};
int main(){
struct person master = {"Master", 23};
return 0;
}
void printInfo(struct person *p){
printf("Name:\t%s\n", p->name);
}
struct person;
typedef void (*fp)(struct person*);
struct person {
char * name;
fp fptr;
};
void afunc( struct person * p ) {
// stuff
}
int main() {
struct person p = { "fred", afunc };
}
You can add the struct person; before the function, but you cannot assign the function in struct person as far as I know,
#include <stdio.h>
struct person;
typedef void (FUNCTYPE)(struct person *);
void printInfo(struct person *);
struct person{
char *name;
int age;
FUNCTYPE *pf;
};
int main(){
struct person master = {"Master", 23, printInfo};
(master.pf)(&master);
return 0;
}
void printInfo(struct person *p){
printf("Name:\t%s\n", p->name);
}
The example above prints Name: Master
The only thing I would add is that all struct pointers have the same width and alignment (6.2.5/27 (C99 Standard)), so you don't actually require the forward definition of the struct.

Resources