Making two objects in C more object-oriented - c

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.

Related

Implementing basic vtable in 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;
}

Mimicking function Overriding in C

To be specific: Function overriding with the ability to call the base overridden methods.
There are two pieces to this. One is the pre-compiled library code (1), and the other is the User of the Library code (2). I'm implementing a smallest possible, classic Person and Employee example here.
Will highly appreciate a response from a hardcore C developer who knows OOP concepts. I'm developing both Library and User of the Library code, so I've control in both places.
1 A) Library Code (Pre-compiled)
person.h
typedef struct Person Person;
struct Person {
char* name;
void (*display)(const Person *self);
};
Person* PersonNew(char* name);
person.c
static void display(const Person *self) {
printf("Name: %s\n", self->name);
}
Person* PersonNew(char* name) {
Person* self = malloc(sizeof(Person));
if(self == NULL) return NULL;
self->name = strdup(name);
self->display = display;
return self;
}
1 B) Library can receive structs with composed Persons via pointers because it's library's responsibility to print these structs (therefore library doesn't know the existence of Employee struct)
void print(Person *person) {
person->display(person); // problem, can only print name, no company
}
2) The user of the Library Code
employee.h
typedef struct Employee Employee;
struct Employee {
Person super;
char* company;
void (*display)(const Employee *self);
};
Employee* EmployeeNew(char* name, char* company);
employee.c
static void display(const Employee *self) {
self->super.display(&self->super); // re-use super display
printf("Company: %s\n", self->company);
}
Employee* EmployeeNew(char* name char* company) {
Employee* employee = malloc(sizeof(Employee));
if(employee == NULL) return NULL;
free(employee->super); // I also have memory leak issue.
employee->super = *PersonNew(name); // library method to get struct with functions.
employee->company = strdup(company);
employee->display = display; // it's own version of display
return employee;
}
main.c
Employee *employee = EmployeeNew("John", "Acme");
print(employee); // problem, calls base method, prints name only
// employee->display(); // works fine, but the requirement is somehow enable library code to be able to call employee version of display.
Since you put Person at the beginning of Employee hence there is no issue, but if you move to other location, it might lead to exception.
The Employee struct will look like:
struct Employee {
char* name;
void (*display)(const Person *self);
char* company;
void (*display)(const Employee *self);
};
Hence when you pass to print, since the print accepts only upper part of Employee:
struct Employee {
char* name;
void (*display)(const Person *self);
};
the call of display will redirect to display of Person. hence there is only name printing.
To override Person's display, let change these two structs to:
struct Person {
void (*display)(const Person *self);
char* name;
};
struct Employee {
void (*display)(const Employee *self);
Person super;
char* company;
};
By moving all operands to beginning of struct, it will work -> but this is dangerous.
A quick and rather unsophisticated way to do it could be something like the following.
typedef struct Person Person;
typedef void (*DISPLAYPROC)(const Person* self);
struct Person {
char* name;
DISPLAYPROC display;
};
void ConstructPerson(Person* self, const char* name);
Person* NewPerson(const char* name);
void person_display(const Person* self);
void person_display(const Person* self) {
printf("Name: %s\n", self->name);
}
void ConstructPerson(Person* self, const char* name)
{
self->name = _strdup(name);
self->display = person_display;
}
Person* NewPerson(const char* name) {
Person* self = (Person *)malloc(sizeof(Person));
if (self == NULL) return NULL;
ConstructPerson(self, name);
return self;
}
void print(Person* person) {
person->display(person); // dispatched at runtime to the right 'display'
}
typedef struct Employee Employee;
struct Employee {
Person super;
char* company;
};
void ConstructEmployee(Employee* self, const char* name, const char* company);
Employee* NewEmployee(const char* name, const char* company);
void employee_display(const Employee* self);
void employee_display(const Employee* self) {
person_display((const Person*)self);
printf("Company: %s\n", self->company);
}
void ConstructEmployee(Employee* self, const char* name, const char* company)
{
ConstructPerson(&self->super, name);
self->super.display = (DISPLAYPROC)employee_display;
self->company = _strdup(company);
}
Employee* NewEmployee(const char* name, const char* company) {
Employee* self = (Employee *)malloc(sizeof(Employee));
if (self == NULL) return NULL;
ConstructEmployee(self, name, company);
return self;
}
int main()
{
Person* person = NewPerson("Bob");
print(person); // calls person_display
Employee* employee = NewEmployee("John", "Acme");
print((Person*)employee); // calls employee_display
free(person);
free(employee);
}
A few notes:
Person is embedded as a field (not a pointer) in Employee so that the casts are legal between Person* and Employee*;
the New/Construct functions separate the initialization from the allocation, which was mentioned in the comments before.
the display functions are made visible (not static) so that a "derived" class can call the "base" function.
Left for the reader to fill in:
Destruct functions paired with the Construct ones, so that the allocated strings get duly freed;
separation of data initialization from "vtable" initialization, so that for example the display pointer is not initialized twice in ConstructEmployee (once in the nested ConstructPerson then overwritten immediately after in ConstructEmployee).
Filling in Destruct function for the review only. Destruction of Derived class first before the Base class.
employee.c
void EmployeeDestruct(Employee *self) {
free(self->company); // destruct derived first
PersonDestruct(&self->super); // then destruct base
}
person.c
void PersonDestruct(Person *self) {
free(self->name);
// free(self); // removing since it'd fail badly for local stack vars
}

How can I define two structs, each one used in the second one ? C language

Can i define two struct and use each one in the other one?
my code below .
I try to use typedef, but not working.
struct Book{
int isbn;
char title[21];
//This is Auther struct usage;
struct Auther bauther[21];
int numofauth;
char section[21];
int copies;
};
struct Auther{
char auth_name[21];
//This is Book struct usage;
struct Book auth_books[21];
};
J:\Collage_Library\main.c|23|error: redefinition of 'struct Auther'|
A struct cannot be inside another struct that is inside the first struct, for the same reason that in the real world you cannot put an object inside another object inside the first object.
An object can refer to another object using a pointer.
For example, a struct Book could have a member that is a pointer to a struct Author or that is an array of pointers to struct Author. That could be declared like this:
struct Book
{
int isbn;
char title[21];
struct Author *bauthor[21]; // Array of pointers to struct Author.
int numofauth;
char section[21];
int copies;
};
Similarly, the struct Author could contain pointers to struct Book:
struct Author
{
char auth_name[21];
struct Book *auth_books[21]; // Array of pointers to struct Book.
};
When you create struct Book or struct Author objects, you will have to fill in the pointers. To do this, you will have to create each of the structures and then assign values to the pointers. For example:
struct Book *b = malloc(sizeof *b);
if (b == NULL) ReportErrorAndExit();
struct Author *a = malloc(sizeof *a);
if (a == NULL) ReportErrorAndExit();
b->isbn = 1234;
strcpy(b->title, "Forward Declarations");
b->bauthor[0] = a; // List a as one of b's authors.
b->numofauth = 1;
strcpy(b->section, "Structures");
b->copies = 1;
strcpy(a->auth_name, "C committee");
a->auth_books[0] = b; // List b as one of a's books.
Here is a simple example with a one-by-one reference.
#include <stdio.h>
struct Book {
char title[21];
struct Auth *auth;
};
struct Auth {
char name[21];
struct Book *book;
};
int main(void)
{
struct Auth a = { "People of God" , NULL };
struct Book b = { "42-line Bible", &a };
a.book = &b;
printf("book title: %s\r\n", b.title);
printf("book author: %s\r\n", b.auth->name);
printf("auth name: %s\r\n", a.name);
printf("auth book: %s\r\n", a.book->title);
return 0;
}

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