Skip to content

Instantly share code, notes, and snippets.

@LukeGary462
Created November 23, 2023 14:10
Show Gist options
  • Select an option

  • Save LukeGary462/a5adb69d1e58bce1b76e22ddf84366d4 to your computer and use it in GitHub Desktop.

Select an option

Save LukeGary462/a5adb69d1e58bce1b76e22ddf84366d4 to your computer and use it in GitHub Desktop.
C11 structure inheritance patterns
/**
* @file: inheritance.c
* @name: Luke Gary
* @company: <company>
* @date: 2023/11/22
********************************************************************************
* @copyright
* Humanity
********************************************************************************
* @brief: practice for oop inheritance in c
* a smattering of inter-web research got to this
*/
#include <stdio.h>
#include <stdint.h>
/**********************************************************************************/
// first with anonymous base struct in derived class
// compiles with: gcc inheritance.c -o inheritance -std=c11
// base class
typedef struct {
int a;
} Base1;
typedef struct {
Base1; // anonymous struct
int b;
} Derived1;
void base1_init(Base1 *self) {self->a = 1;}
void base1_foo(Base1 *self) {self->a++;}
void derived1_init(Derived1 *self) {self->a = 1; self->b = 2;}
void test1(void) {
printf("\n\n\n");
printf("METHOD 1\n");
printf("Inheritance with anonymous struct base in derived class\n");
printf("typedef struct {\n");
printf(" int a;\n");
printf("} Base1;\n");
printf("typedef struct {\n");
printf(" Base1; // anonymous struct\n");
printf(" int b;\n");
printf("} Derived1;\n");
printf("void base1_init(Base1 *self) {self->a = 1;}\n");
printf("void base1_foo(Base1 *self) {self->a++;}\n");
printf("void derived1_init(Derived1 *self) {self->a = 1; self->b = 2;}\n");
printf(\
"test1(){\n"
"\tDerived1 d; // step 0\n"
"\tderived1_init(&d); // step 1\n"
"\tbase1_foo(&d); // step 2\n");
printf("1 - Derived Class Initialization:\n");
Derived1 d;
derived1_init(&d);
printf(
"d.a = %u\nd.b = %u\n",
d.a, d.b);
printf("2 - calling baseclass method without cast of derived object ref:\n");
base1_foo(&d);
printf(
"d.a = %u\nd.b = %u\n",
d.a, d.b);
printf("\nNOTES:\n");
printf("Compiles and runs properly, but generates warnings below:\n");
printf("$ gcc inheritance.c -o inheritance -std=c11\n");
printf("inheritance.c: In function 'test1':\n");
printf("inheritance.c:46:15: warning: passing argument 1 of 'base1_foo' from incompatible pointer type [-Wincompatible-pointer-types]\n");
printf(" 46 | base1_foo(&d);\n");
printf(" | ^~\n");
printf(" | |\n");
printf(" | Derived1 *\n");
printf("inheritance.c:35:23: note: expected 'Base1 *' but argument is of type 'Derived1 *'\n");
printf(" 35 | void base1_foo(Base1 *self) {self->a++;}\n");
printf(" | ~~~~~~~^~~~\n");
printf("Changing base1_foo(&d) to base1_foo((Base1 *)&d) solves the warning but defeats the purpose of this\n");
printf("since getting the derived object immediate access to base methods is the goal without casts\n");
printf("\n\nEvidently however, the C11 standard says the former is kosher\n");
printf("\tn1570 6.7.1.1.15\n");
printf("\tA pointer to a structure object, suitably converted, points to its initial member \n");
printf("\t(or if that member is a bit-field, then to the unit in which it resides), and \n");
printf("\tvice versa. There may be unnamed padding within a structure object, but not at its beginning.\n");
}
/**********************************************************************************/
// alternate, builds, but has a runtime bug when accessing base member from derived
typedef struct {
int a;
} Base2;
typedef struct {
Base2 base;
int b;
} Derived2;
void base2_init(Base2 *self) {self->a = 2;}
void derived2_init(Derived2 *self) {
base2_init((Base2 *)self);
self->b = 2;
}
void test2(void) {
printf("\n\n\n");
printf("METHOD 2\n");
printf("Inheritance with baseclass member in derived with typecasts\n");
Derived2 d2;
derived2_init(&d2);
// printf("d2.a = %u\nd2.b = %u\n", d2.a, d2.b); // error: 'Derived2' has no member named 'a'
printf("d2.a = does not compile\nproduces \"error: 'Derived2' has no member named 'a'\"\n");
}
/**********************************************************************************/
// alternate, composition over inheritance. Not as nice to use but is the most concervative
// approach. This also means that a derived object may not use a base class method like this:
// baseclass_foo(&derived_obj);
// instead it must be:
// baseclass_foo(&derived_obj.base);
typedef struct {
int a;
} Base3;
typedef struct {
Base3 base;
int b;
} Derived3;
void base3_init(Base3 *self) {self->a = 3;}
void base3_foo(Base3 *self) {self->a++;}
void derived3_init(Derived3 *self) {
base3_init(&self->base);
self->b = 3;
}
void test3(void) {
printf("\n\n\nMETHOD 3\n");
printf("composition over inheritance\n");
printf("alternate, composition over inheritance. Not as nice to use but is the most concervative\n");
printf("approach. This also means that a derived object may not use a base class method like this:\n");
printf("baseclass_foo(&derived_obj);\n");
printf("instead it must be:\n");
printf("baseclass_foo(&derived_obj.base);\n");
printf("1 - Derived Class Initialization:\n");
Derived3 d;
derived3_init(&d);
printf(
"d.base.a = %u\nd.b = %u\n",
d.base.a, d.b);
printf("2 - calling baseclass method directly from base:\n");
printf("\te.g. - base3_foo(&d.base);\n");
base3_foo(&d.base);
printf(
"d.base.a = %u\nd.b = %u\n",
d.base.a, d.b);
}
void test4(void) {
printf("\n\n\nMETHOD 3 Alternate method calls, but access is the same\n");
printf("\te.g. - base3_foo((Base3 *)&d);\n");
Derived3 d;
derived3_init(&d);
base3_foo((Base3 *)&d);
printf(
"d.base.a = %u\nd.b = %u\n",
d.base.a, d.b);
printf(
"((Base3 *)&d)->a = %u\nd.b = %u\n",
((Base3 *)&d)->a, d.b);
}
void main(void) {
test1();
test2();
test3();
test4();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment