Created
November 23, 2023 14:10
-
-
Save LukeGary462/a5adb69d1e58bce1b76e22ddf84366d4 to your computer and use it in GitHub Desktop.
C11 structure inheritance patterns
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @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