Last active
September 6, 2021 13:54
-
-
Save Lisoph/d7ecf7fd67ed1b8c15ab876183bfe07a to your computer and use it in GitHub Desktop.
CONTAINER_OF macro to downcast types more easily in C
This file contains 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
//===--------------------~ Source ~--------------------===// | |
#include <stdint.h> | |
#ifndef offsetof | |
#define offsetof(type, member) ((uintptr_t)&((type*)0)->member) | |
#endif | |
#define CONTAINER_OF_CONST(member_ptr, container_ty, member_name) \ | |
((container_ty const*) ((uintptr_t) member_ptr - offsetof(container_ty, member_name))) | |
#define CONTAINER_OF(member_ptr, container_ty, member_name) \ | |
((container_ty *) ((uintptr_t) member_ptr - offsetof(container_ty, member_name))) | |
//===--------------------~ Usage ~--------------------===// | |
// Parent type | |
typedef struct { | |
float x, y; | |
float width, height; | |
} Widget; | |
// Child type aka. container, because it contains the parent (widget). | |
typedef struct { | |
char const *label; | |
Widget widget; | |
int times_clicked; | |
} ButtonWidget; | |
// Cast a Widget* to a ButtonWidget* | |
// It doesn't matter where widget is declared in the struct, | |
// it doesn't have to be the first member. | |
#define WIDGET_DOWNCAST_BUTTON(ptr) CONTAINER_OF(ptr, ButtonWidget, widget) | |
// Cast a ButtonWidget* to a Widget* | |
#define BUTTON_UPCAST_WIDGET(ptr) (&(ptr)->widget) | |
//===--------------------~ Example ~--------------------===// | |
void button_on_click(Widget *w) { | |
ButtonWidget *button = WIDGET_DOWNCAST_BUTTON(w); | |
++button->times_clicked; | |
} | |
void example() { | |
ButtonWidget bw = { | |
.label = "my button", | |
.widget = { | |
.x = .0f, | |
.y = .0f, | |
.width = 32.0f, | |
.height = 32.0f, | |
}, | |
.times_clicked = 0, | |
}; | |
button_on_click(BUTTON_UPCAST_WIDGET(&bw)); | |
assert(bw.times_clicked == 1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment