-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement "placement new"-style constructors generation #79
base: main
Are you sure you want to change the base?
Implement "placement new"-style constructors generation #79
Conversation
Not directly related, but this assumes the C and C++ structs all have exactly the same memory layout. Which makes sense, but I've found this bit of code puzzling in that context:
Why do we need to do per-member copy instead of a simply memcpy? |
Thanks - this looks good to me! On the questions front:
Hmm... this is a complicated one. My thinking at the moment is:
That's a very good question, and it ties into the naming problem as well I think. Basically, my feeling is that if we go for Conversely though, that does mean that the user ends up making So to summarize, I've personally got two preferred options, I think:
My gut feeling is that 2 is probably the better option (as there aren't really very many types that require the full As for this:
Hmm... to be honest, "why indeed?" is the answer that springs to mind! I suspect when I wrote that code there was still some uncertainty in my head about how much I could rely on keeping structure layouts the same, and also the possibility that some types would need custom conversion logic at some point. It does feel like a Thanks! |
I personally prefer this option, if nothing else than simply to avoid "dummy" destructor calls when using wrappers in higher-level languaegs like C#. But we really should add some metadata to mark function as constructor/destructor/initializer, for purposes of generating bindings from C to another languages. Right now, the only way to know if a function is a constructor is to check if the function's
Just thinking out loud - this will probably only work when used directly from C or a similar low-level language, as a typical C# wrapper would wrap the Init() method with a struct constructor anyway, so if the behavior changes, there's not a lot of ways to communicate that suddenly you also need to call an extra Dispose()-like method.
I've tried it locally and noticed no problems! Also looking at that entire part of code, there's entire machinery to generate methods like |
Just recalled that there are a few silly destructors in Dear ImGui that are actually something like "block finishers", and duplicate existing struct functions: ImGuiListClipper::~ImGuiListClipper()
{
End();
} I'm fairly sure those should be special-cased and not emitted as destructors at all, and instead the users should be asked to just call End() themselves. After all, you have to call Begin() manually, it's not included in the constructor or anything, so why is it different for End()? |
I believe clipper is the only case, and maybe due to legacy reason, but it is therefore ok for it to only call End. |
@@ -246,6 +245,22 @@ def convert_header( | |||
'ImRect', | |||
'ImGuiListClipperRange' | |||
]) | |||
|
|||
# Mark certain types as needing placement new constructors that initialize the memory block passed into them | |||
mod_mark_placement_constructor_structs.apply(dom_root, [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this list was given by @ocornut . lets not depend on that.
how can i figuire out this list and/or knows when to update it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the ask is to provide additional detail in a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't list we can automatically figure out what goes in the list.
I can try to notify this project if new types appear that are likely to require constructors, otherwise people will eventually request them.
Personally I like 1 as its less Cognitive load on the dev but 2 is fine as long as it's explicitly mention in the readme of this repo what does construct/Destruct and init mean. what's the difference between them. why would u use them at all. |
also, if this repo is gonna last for many years to come...it's not a minor maintenance. let's not add more work. When these things changes (i.e. constructor isn't required it's init now) you may not be thinking about dear_bindings....if something changes from init to constructor...that's worst...now u get memory leak.....unless imgui itself have a way of differentiating between them programmatically i.e. is it a constructor or a init i wouldn't go there. |
oh. so there is a way, in that case, nevermind :)
yeah but you as a c# wrapper owner have a keep an eye on the changelog (or worst the code) to ensure c# wrapper is consistent every imgui release. they do a good job with changelog so maybe I am just being paranoid. |
If I want to be ultra paranoid, I can take the previous version metadata and check if any _Init() functions have become _Construct. Plus, yes, the changelogs are usually excellent, I don't see this becoming a constant problem |
the only way I can think of to solve this problem is to re-name the struct based on if it has intit, construct or nothing. (for those who aren't familiar with C# tldr structs over here has constructors but no destructors).....personally i like compiler error when upgrading imgui over runtime error OR memory leak issue :) ZimM-LostPolygon, how do those struct behaves when they are passed by copy? do they call the constructor again? EDIT: you have to do |
everyone does, but appending prefixes to struct based on that sounds very ugly. I'd rather do the diff on every imgui release - normally you'd still want to do that to check for any other things that need tweaking.
It's simply disallowed - if a by-value struct has a destructor, dear_bindings will throw an error. |
it's not just you (binding owner) who has to do that, it's everyone who uses your bindings....and i understand that's the right thing to do (and i do that as well) but given imgui doesn't follow semantic versioning + imgui release notes != imgui binding release notes (e.g. adding constructor+distructor might not be considered as breaking change)....it's easy to miss...at-least for new ppl. |
anyway, there is no right or wrong over here, it's just personal preferences. at the end of the day if we write down something about it in readme for new ppl, i think it will be fine. also, i am assuming these structs rarely changes. e.g. readme questions = as a project owner/binding owner what do i need to look for in releases, what's init, what's constructor, etc etc. |
I don't think I'm too worried about bindings for a non-C language (e.g. C#) running into the case where a type changes from not needing a constructor/destructor to needing one because that seems pretty straightforward to automate - if we emit an And in C the only case that's really problematic is "class that had no init or constructor suddenly needs one or the other", as for the other cases (init becomes constructor, etc) there will be a compile error trying to call the old function. If we standardise on "all classes have some sort of init/construct function" then this basically disappears as a problem, I suspect. (which feels reasonable... whilst there are cases where for performance you might want to deliberately leave a structure uninitialised and then overwrite all the values yourself, that's still doable just by ignoring the init function, and I'm not sure we want to be encouraging that sort of behaviour anyway because it's even more hilariously brittle in the face of structure changes) Sure, there will probably be cases that slip through the cracks but pretty much any scheme short of the suggestion above of renaming types based on constructor presence is going to run into that I suspect. And with non-C bindings the binding generator for the target language could always implement that scheme if they felt it was necessary for their use-case - there's no need for Dear Bindings to enforce it. |
agree
maybe there is an innovative approach with new c# language version to do that but as far as I am aware this line won't be true. :) |
Based on the idea and the list of types from here:
#55 (comment)
Generated code looks like this:
ImFontConfig_ImFontConfig
,ImFontConfig_Construct
,ImFontConfig_New
, something else? How do we differentiate between placement new constructors and by-value constructors that return an already constructed value? Should we even differentiate? (personally I like the consistency of using the sameImFontConfig_ImFontConfig
style for everything, but then we also already haveImVector_Construct
)