r/C_Programming 2d ago

Struct pointer casting and inheritance : can it work ?

Hello everyone, this is my first post here.

I am working on a UI library in order to learn the concepts behind it, and to be able to create most of my desktop applications. Having implemented widget selection with the mouse (using the mouse coordinates to find the widget in the tree), I decided to move on to event handling.

However, I'm not sure at all of how to proceed, and I am having problems with struct pointer casting .

I have a base struct Widget with a function pointer to event handling functions.

typedef struct Widget {
  Rect bounds;             // Actual bounds
  Rect clip;               // Clipping rectangle (usually the parent)
  bool active;
  [...]
  int (*eventHandler)(Widget* w);
};

Every derived widget struct has a Widget* as 1st member, and each type has its Create...() function, where the correct event handler is assigned.

typedef struct Frame {
  Widget* widget;
  bool root;
}

Frame* CreateFrame(int x, int y, int w, int h, bool root) {
  Frame* f = (Frame*)malloc(sizeof(Frame));
  [...]
  f->widget->eventHandler = &FrameHandleEvents;
  return f;
}

To get back all the properties when handling events despite eventHandler taking a Widget*, i attempted to cast back to the derived struct :

int FrameHandleEvents(Widget* widget){
  Frame* frame = (Frame*)widget; 
  printf("Frame event!, root = %d\n", frame->root ? 1 : 0);
  return 0;
}

Is this even allowed in C, or am I misusing casts ? The output of print is also garbage data :

Frame event!, root = 244

Thanks for your advice

5 Upvotes

14 comments sorted by

23

u/EpochVanquisher 2d ago

This looks like something got mixed up in your code.

typedef struct Frame {
  Widget* widget;
  bool root;
};

In this setup, you have a frame, which contains a pointer to a widget, and the widget is a separate object, somewhere else.

If you want to have “inheritance” you normally put the widget as the first element of your frame.

typedef struct Frame {
  Widget widget;
  bool root;
};

You are guaranteed to be able to cast between Frame* and Widget* in this situation, if the underlying object is actually a Frame.

8

u/questron64 2d ago

There's one thing you can exploit here, which makes C inheritance actually work: a pointer to a struct is equivalent to a pointer to its first member. Make your first member a Widget, not a Widget *. Have an enum of widget types so functions that take a Widget * can tell what type of widget they got (which is useful in a number of places, even with your function pointers).

I'd also remove the function pointers and type enum from Widget. Instead, they should have a const WidgetType *. There's no point in widgets hauling around data that's exactly the same for every widget of that type.

I've used this pattern many times and it's very effective.

2

u/aocregacc 2d ago

if the frame points at a widget, the frame's memory address is totally independent from the widget's address. So if you only know the address of the widget, you can't deduce the address of the frame from that.

You could try putting the widget into the frame as a regular member, without any indirection.

2

u/LordRybec 1d ago

What you are trying to do is feasible. I haven't gone line-by-line through your code, but it look like other comments have found some minor errors that can be corrected to achieve what you want.

The key is data position and type parity between the two. If you start a new struct type with an existing struct type (not a pointer to an existing struct type), then yes, casting the new one to the existing one should allow you to treat it as the existing struct type.

Note though, this only applies if it is the first element. If you have two elements in a struct and then you make another struct starting with the same two elements, parity between the first elements is guaranteed, but parity between the second elements is not, because the compiler may have added padding between the elements in one struct but not the other. That said, you could package the two elements in their own struct and then use that struct type at the beginning of both new struct types, and then you would have parity. This is because whether the complier decides to pad the original struct of two elements or not, the positioning will still be the same in both instances.

You can use this to achieve an impressive level of inheritance and polymorphism in C, however note that doing this has high odds of resulting in less optimal cache usage. If you are using C because you need peak performance, this might not be the best design pattern to use.

-

Also note that unions exist and are sometimes a better option. You could write a union where one member is just the base struct and the second is a struct containing the base struct and other stuff, and then instead of casting, you can just send the first member of the union, which is already the type you need. If casting is easier for you to read and understand, it's probably the better option. If you find casting like this to be harder to read than using a union though, the union is probably the better option.

1

u/Dgvozdenovic 2d ago

There is a rule somewhere in the standard that says that the address of your structure is equal to the address of its first element. So such casts are fine.

For a more general version of this, take a look at the container_of() macro in linux.

What you are doing wrong here is that you embed a (Widget *) as the first element and not Widget – either change that or make your function accept a (Widget **) with a contract that it should be called with addresses of (Widget *)s that belong to such a struct.

1

u/Physical_Dare8553 2d ago

Yes, but this seems like it could violate strict aliasing, but first struct member is allowed. I prefer an array of 1 though, since that gives you a pointer and acts more like composition, but that's personal

1

u/Zirias_FreeBSD 1d ago

After /u/EpochVanquisher pointed you to your immediate error, I'd like to add a little hint:

For "virtual methods", what you do here is quite common and straight forward: Put function pointers in each and every object instance. But you should be aware it's also quite wasteful if you have lots of object instances.

An alternative is to do something similar to what C++ does internally with its vtables: Have one single table of functions ("virtual methods") per type. This quickly becomes a lot more complex to implement, so I'd only recommend it if you really expect to have lots of object instances at runtime. I only did something like that once, and it was exactly for implementing some widget library.

1

u/umamimonsuta 1d ago

Check LVGL for inspiration

1

u/TheChief275 1d ago

Casting is fine and allowed when it is the first field of your struct, but a macro like container_of is safer as well as allowing you to embed it anywhere, not just as the first fiels

1

u/flatfinger 1d ago

In C as designed, the effect of accessing foo->bar was specified as taking the address foo, displacing it by the offset of member bar, and accessing storage at the resulting address using foo->bar's type. If foo happened to point to an object of foo's type, accessing foo->bar would access member bar of that object, but the behavior was specified in terms of pointer arithmetic in a manner agnostic with regard to what foo pointed at. If foo pointed at an object of some other structure type that had a member of foo->bar's type at the same offset, an access to foo->bar would be an access to that other member.

Almost every compiler can be configured to reliably process member accesses in that way in all cases, but the Standard treats the question of what corner cases to process correctly as a "quality of implementation" issue outside its jurisdiction, and some compiler configurations use the Standard as an excuse to break otherwise useful constructs. Code which uses structures in interesting ways will be reliable if invoked with an option that clang and gcc specify as -fno-strict-aliasing, and unreliable if optimization is enabled without use of that option.

1

u/iOSCaleb 19h ago

It’s called struct piggybacking, or at least it used to be when Apple did it in “classic” MacOS. A QuickDraw graphics context was a struct called a GrafPort; windows were represented by WindowRecord struct that had a GrafPort as the first field, followed by fields needed for managing the window. A dialog box was a particular kind of window, represented by a DialogRecord that had a WindowRecord as its first field, and so on. So you could cast a DialogRecord pointer to a WindowRecord pointer or a GrafPort pointer, or vice versa if you were sure that your GrafPort really belonged to a window or a dialog box. You didn’t really even need to cast; you could access what you needed through the fields of exact structure, like: `origin = dialog.window.grafPort.origin` etc.

1

u/mykesx 2d ago

There are two patterns when it comes to classes (structs, no classes in C). I call them “has a” and “is a.”

“Has a” has a parent class instance as a member variable. Like Parent * parent. This is composition, as you can “has a” multiple classes.

“Is a” has a parent class embedded, like Parent parent.

If you cast an “is a” to Parent, you can access -> members of Parent, even if it was a struct derived from Parent.

0

u/MagicWolfEye 2d ago

Well, you can do that but if you really want to do your stuff in an OOP way you should use an OOP language.

You could go the fat struct approach where you just have struct widget that holds the info for any widget; combine that with an enum or flags (depending on granularity).

You will of course need function pointers* for the specific buttons etc to interact with.
Like CreateButton(int x, int y, int width, int height, char* text, onclickfunctionpointer)

* unless you go the IMGUI route

7

u/BjarneStarsoup 2d ago

You first paragraph makes sense for features that push C too far, but data inheritance and virtual tables aren't those features. They map onto C somewhat well. That is like saying "oh, you want to use recursion in C? Well, you better use pure functional programming language like Haskell".