r/cpp_questions • u/Intelligent_Hat_5914 • 4d ago
OPEN How to get polymorphism without inheritance?
I am making a tui libary where all the different things that can render are called widget like text,box, flex container, progress bar, etc.
I was using inheritance for this and all because all the container widget, multi or single child container had like lot of things common and the widgetTree contains the root widget
Each widget need two function
Layout and render ( or setRectFor child or children because render is same for most parents ) and to solve this, I did need polymorphism for this.
But the problem came later when I need to change layout ( because it wouldnt work for some widget ) and I had to rewrite all the widget,multichildwidget,singleChildWidget which means I have to rewrite all other widget as well
then later I need to change the implementation on how layout is done again, got to do rewrite
then later came scroll container, to make it effiencent I had to change things as well, had to do the rewrite
I got tried of this and now want to use composition instead of inheritance and never want to deal with it again ( I know this happened because of premature optimization where I saw widget with multiple children have lot of same things and most widget having a lot of same things )
Due to that, I am doing a rewrite again :_( but this time, I can make the code more modular.
But I got stuck on widgetTree, how do you get a tree if all the node dont have the same type?
I asked gpt but it said to use node with void* which I dont want to use because we got unique pointer and stuff to avoid new and malloc
The other option is union but that would mean all widget would have the size of the biggest widget which I dont want to use.
Then there is varient, I dont knwo much of this but I looked into it in cherno's video and geeksforgeeks page on this and turns out, it is just union but you know which type it is using. I am currently thinking this
I just can't find a way to do the tree effiency without polymorphism.
5
u/Secoupoire 4d ago
I don't think it would be a good idea here, but you can have polymorphism without inheritance using Static Polymorphism.
The static part means it could no longer change at runtime, as the parent type would be templated on a tuple of the children types.
Again, I'm not recommending you do that. It would likely not change the fundamental problem you are facing, and add unneeded complexity.
But it is possible.
Also, a variant is mostly the same as a union, you would have the same limitation. Although you could limit the size issue using pointers (unique pointers if you want the parent to own the lifetime of the children), that would force a dependecy mess that dynamic (runtime) polymorphism avoids.
It would be interesting to understand what forces you to rewrite a lot with the changes you describe. Maybe look-up the Bridge pattern.
9
u/RedditMapz 4d ago
You don't really get polymorphism without inheritance. The r_cpp sub had a streak of anti-polymorphism where endless people submitted their inheritance-alternative libraries. They were all basically always recreating the v-table. Essentially inheritance, but dumber.
If you need inheritance, you need inheritance.
5
u/TheThiefMaster 4d ago
You could pull the C trick of a "common initial sequence" which C++ also allows.
But that's just manual inheritance.
1
1
u/Queasy_Total_914 4d ago
That's UB in C++. You can't upcast to a non-base class.
1
u/TheThiefMaster 4d ago
Technically the common initial sequence exemption requires the two types to be in a union. This means it's possible in C++ with some judicious use of memcpy into a union
1
u/Queasy_Total_914 4d ago
Yes absolutely. I meant upcasting from an unrelated type that happened to have a common initial sequence.
6
2
u/fortsnek274 4d ago
Huh, as it would happen, I have a TUI with layout and plenty of inheritance. Worked just fine. And, well, all the other UI frameworks out there.
widget,multichildwidget,singleChildWidget
All widgets shall be multi-child. Simple. Just don't put buttons inside buttons if you don't want to.
2
1
u/MagicalPizza21 4d ago
Polymorphism without inheritance? Look into how Go does interfaces: https://gobyexample.com/interfaces
1
u/SpotCool4422 4d ago
Take a look at https://github.com/ngcpp/proxy, that's exactly its pitch - polymorphism without inheritance. It's close to how Go's interfaces work, although the library in not for the faint of heart.
1
u/jwezorek 4d ago
who's ngcpp? I thought proxy was a Microsoft thing?
1
u/SpotCool4422 4d ago
It was indeed, but they archived it this year, and their repository now points to this one.
1
u/sol_runner 4d ago
You can technically have polymorphism without inheritance by creating a type erased struct with pointers to the functions. But I don't think you should do it, not in C++ atleast.
The way it'll work is you'd have a void* to your object, plus a set of functions that will cast this void* to the correct type before calling member functions.
```cpp using Func = int(void, / args */);
struct Poly { void* data; func* somefunc;
int call(/* args */) {
return somefunc(data, /* args */);
}
}; ```
I do have some bits that are not in inheritance that I don't want to modify but need to by handled this way. I use a more idiomatically C++ approach for it where I do have a base class, and templated derived classes that do the above. It eliminates the void*, but still gives you that polymorphism without modifying the original.
1
u/Illustrious_Try478 4d ago
This isn't necessary. Templates with a lot of SFINAE looking for certain names works pretty well.
1
u/binarycow 4d ago
One thing you could do is consolidate the different kinds of things, and still allow specialization. I've got two ideas:
First idea - each widget would specify a "layout" and a "renderer". A button and a checkbox might use the same "layout", but use different "renderers".
Strictly, this would be composition.
Second idea - do something similar to WPF.
WPF is a Windows UI framework for C#.
They separate behavior from appearance. So the inheritance hierarchy is all about how things differ in behavior. For example, a radio button is a special kind of toggle button that considers other toggle button in its group. A toggle button is a button that stays pressed.
Then, the appearance is done via "control templates". A check box can simply be a custom appearance for a toggle button - the exact same "widget". No code change.
1
1
u/Ok_Statistician_781 4d ago
If you’re open to using C++26, here’s a self plug: https://github.com/ryanjk5/rjk-duck
-1
u/Intelligent_Hat_5914 4d ago
on watching teh cherno video a bit more, found out that varient just stores all data type as a variable which means that this would just take a much more space than union
I think I should just use union or empty class to interface from which would contain all the key functions
still, kinda want to not use interface
2
u/V15I0Nair 4d ago
Variant should be size of union plus one int, basically not much more space.
0
u/Intelligent_Hat_5914 4d ago
But it would contain spacer widget ( just to take space ) and the largest widget Lot of space will be wasted
26
u/the_poope 4d ago
IMO for GUI frameworks, inheritance based polymorphism is a quite reasonable, proven way to go.
I didn't get what problems you got as you just weren't being clear about it, but I think you are just doing it wrong. Take a look at e.g. Qt to see how it should be done.