-
-
Notifications
You must be signed in to change notification settings - Fork 10.6k
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
API support for string slice parameters instead of null-terminated strings #494
Comments
string_view will never be used because its C++11 |
Use std::string to produce your null terminated string: std::string(start, On Wed, 20 Jan 2016 at 07:36 Paul [email protected] wrote:
|
I'm not suggesting that ImGui should use |
This will cause many many allocations and string copies, which can be avoided with this change. |
edited @bitshifter I'll have to get back to you but give me some time. This is potentially a big change to follow on and I don't have sufficient bandwidth at the moment. One of my concern, which is arguably hard to measure, is that I don't want to give the impression to people skimming at the library that this is a high-level library that uses lots of mysterious and heavy data structures. Note that I am merely talking about the impression here. C/C++ developers are very reluctant to use libraries and ImGui aim to combat that. char* carry a natural feeling of accessibility to many devs. Now, your point are valid and I don't want to reject it. Interested in your progress and thoughts down the line. @Cthutu : very slow very heavy. The whole point of slices is to avoid it. |
@ocornut Yeah, I totally agree/understand your concern, so wouldn't be offended if the idea is ultimately rejected :) I'll have a look at converting the remaining char* at some point, some of them might be a bit less straight forward than the labels so it would be interesting to see how it pans out. |
Some ideas:
|
A potential plan B (IF we can't get plan A to work) would be to make the necessary adjustments to master so that maintaining a fork is painless. I am also open to make that sort of work easier. |
Good point about function call overhead in debug builds. I wonder if anything can be done about the function call overhead of the ImStr constructors themselves. I've committed some updates to my branch based on your feedback:
I think the lazy evaluation works well, as many method calls don't need End to be set anyway, e.g. if all they are doing is calculating a hash. I didn't do anything specific with TextUnformatted, it currently does a CalcEnd(), it was already doing a strlen before so no change in behaviour here. Regarding a fork, it seems reasonable to have gestate this in a separate branch and see if there's any demand for it. I'm not sure what would make maintenance easier. Code wise, the changes are reasonably straight forward, the only problem areas were were the new function signature would match old code (e.g. the new CalcTextSize(ImStr, bool, float) function can be called with old CalcTextSize(char*, NULL) and the NULL will be used as the bool parameter). Some RenderText calls also had this problem. They're pretty easy to spot though. Of course there maybe other things I haven't noticed yet. I think what would probably help most with maintaining a fork is probably more along the infrastructure side. If there was someway to automate merging and building commits to master against the imstr branch that would remove a lot of manual work. It's possible that github/travis already does this. |
There is a bug in your And why doesn't ImStrDup use CalcEnd itself? |
@ratchetfreak The intention is that ImStrdup and CalcEnd are only used internally by ImGui, which means it's easier to expect preconditions like ImStr.Begin being non-null before CalcEnd is called. This is effectively the case in ImGui already (master, not my branch), often a text_end value is calculated by calling strlen on a char* text parameter, it is expected that the char* parameter is not null, but this is not checked at runtime. Still, I am tempted to make CalcEnd a function which is completely internal to ImGui to prevent people from using it externally, since it is expected to be used in a specific way. I've changed the signature of ImStrdup so hopefully it's clearer that it should be given a valid End pointer. The main reason I don't call CalcEnd in there is I think it's preferable that End is calculated as soon as possible if it's needed, to avoid repeatedly recalculating it in leaf functions like ImStrdup. @ocornut I've made a few more commits porting more of the API over to use ImStr. It's mostly done now, just combo lists and list boxes and text filtering to go I think. |
There is also formatting to be done I suggest adding a
|
Yes that's true. I was intentionally leaving the format strings as char*, but as you point out the format copy is only required if End is actually set to something, so that doesn't seem so bad. |
A small update: I got a branch of the Rust imgui wrapper calling through my branch of imgui so that strings are passed from Rust to C++ without needed special macros in Rust or copying of strings. I also branched the cimgui library as well, since that's how Rust talks to imgui as it can't call C++ directly. I made some observations from completing this work that I thought it might be useful to share. I didn't end up changing any of the methods that take a format string to use an
On the C++ side of things, I looked up some ImGui code I wrote a few months for a project that used string slices heavily. This code wasn't using my ImGui::Text("%.*s", str.size(), str.data()); I used string literal format strings and I was able to call ImGui methods with string slices parameters by using
|
I like slices, but having a function call silently invoke a constructor seems like a bad idea. It seems like it would be better to have another function (or overload) that takes a pointer to the end of the string or a size. |
Sorry @bitshifter I haven't got around to react and help much with this topic. It's a very useful and interesting thing but just far low in the priority list from my angle at the moment. I'm hoping that keeping this branch alive and sync isn't too painful? At minimum if you think there are way I can help making updates less painful let me know. |
@josiahmanson my goals with this change is to avoid breaking user code as much as possible, avoid greatly increase the size of the API (i.e. through overloads) and avoid impacting users who just want to use char* (by requiring an end of string pointer). The ImStr type was the only way I could come up with to achieve this. A quick grep found around 140 functions which take char* parameters. The char* parameter is usually first, or followed by var args, which means I can't use a default value for end of string most of the time. Providing and alternative function that takes an end of string parameter would greatly increase the size of the API. So for now the silent constructor seems like the approach that best meets my goals. Keep in mind that the constructor doesn't do much other than assign two member variables. @ocornut My intention was to just update the branch when a new dear imgui release was made. I don't think that's happened since I made the branch (unless I missed it) so I haven't synced so far. I do have a PR which merges the latest changes from master and builds them. So far everything merges cleanly, so I think it's mostly new functionality that I'll have to deal with. I'll let you know how it goes when I try updating. |
@ocornut I noticed 1.49 was out so I merged to latest, as it turns out I'd also missed 1.48. The PR I set up didn't do what I thought (was merging master against my unmodified master), so there were quite a few conflicts although most of them were simple to resolve. I can't think of much that would reduce the number of conflicts. I think having comments on the same line as declarations probably causes a conflict for a comment change. Also lack of white space between function declarations might be confusing git a bit. In saying that, changing style now would also generate a lot more conflicts! I think I'll set up a PR against your master to keep track of breaking changes. |
I am getting awfully tempted to bite the bullet and merge the part that would typedef ImStr to |
That would make merging imgui.h easier. It might pave the way for this change too I guess. There's a couple of caveats; printf format strings are still |
I'd like just like to chime in my use case for this as well. I've recently started using imgui with rust for my project, and I'm really excited about the prospect of this branch getting merged someday. Right now it's quite painful to write im_str!("text") instead of "text" on the rust side of application development. Anyways thanks for working on this, if you need someone to do some work on this I can work on it, just need some direction on what's needed next. |
Thanks @bjadamson, you can always use my https://github.com/bitshifter/imgui-rs/tree/imstr branch to use this feature, however I haven't merged with imgui-rs or imgui in a long time so it's not very actively maintained right now. As far as work to do, if you're interested in updating the branches for imgui, cimgui and imgui-rs I'd merge them in. I'm not sure how much work is involved at this point, there's probably a lot of conflicts which might be confusing if you aren't familiar with the code |
Is this issue left abandoned? I think it'll be very useful. What is the current status of |
It's not abandoned (the issue is open) but stagnant and low priority at this point. It's very useful for Rust users but has a non-trivial cognitive load for C++ users so it is a really hard move to make considering dear imgui users are mostly C++ centered. From my point of view there's enough workload on dear imgui that putting that sort of weight in favor of a potential new user base is a hard price to pay. The fact that there's a Rust macro hack (used by imgui-rs) to workaround the problem, however not elegant, reduce the pressure to solve this. It's a very frustrating situation because the PR #683 by bitshifter does everything it needs to do correctly, but I think it is more pragmatic to defer that decision until a more major imgui refactor (e.g. 2.0). (One possible way forward to keep this maintained would be to consider partly-automating the change so the ImStr version can be derived from the master version + a smaller patch, presumably easier to maintain.?) |
FWIW, string_view is getting used more frequently nowadays, so C++ users of string_view are faced with the extra load of copying the string to add a terminator. I'm not pushing for higher priority, just want to mention working around string_view and ImGui together is also a cognitive load :) |
That's a fair point, although I presume the majority of imgui usage relies on a combination of literals + generated strings (via sprintf or another mechanism) in both cases the strings would end up being zero terminated (and for stuff like |
In my code I am now extensively using the rather verbose TextUnformatted. At first it was a workaround for string_view, and then because I realized all my Text("trivial") calls were subject to formatting and therefore a call to sprintf. I'm not sure how to pass non-zero-terminated values to ImGui::Text? |
You are right I meant TextUnformatted()
You can use Text(“%.*s”, end-begin, end); but it is a waste of CPU compared to just using TextUnformatted(). If you have any suggestion for a better name let me know (maybe create a new thread or we can discuss it elsewhere).
|
A terrible idea I had around a name for TextUnformatted was two versions of Text.
|
Yeah :/ I'd prefer to avoid that sort of thing especially for such a common function and as we are trying to make imgui friendly and naturally bindable to many languages without being scary, and still obvious to step into etc. |
Some things to note:
Imho, in the presence of std's charconv (C++17), which operates on character ranges instead of null-terminating strings, and 3th party libraries such as fmt, supporting both |
I do agree string_view is good change going in the right direction and should head toward becoming a well used standard. It's however not in the DNA of imgui to use C++ constructs. It would frighten some user who explicitly want to have zero dependency on STL (those users constitute a fair portion of professional users of dear imgui). Anything STL also tends to have overhead in not optimized builds so they are not exactly as lightweight as they can be. (Also For the above reasons and everything well discussed here would be more sane to have our own implementation of string_view and PR #683 is taking the right approach, we can perfectly make it constructable from std::string_view as well. I already mentioned this is an important change, which I think could be tied to an important release (2.0). It's just too out of scope in the short term, when the 1.7x line is released and well polished we can start considering our options for future priorities. |
If you know the string at compile time (e.g., formatting), you can calculate the string length at compile time by using the array type
Yes indeed. In fact the minimum requirement ImGui requires to (in)directly support P.S.:
If you ever decide to support general forward iterators, all the code will just keep working ;-) |
Has there been any movement on this in the intervening 2 years since the last comment, or is it still waiting for a major breaking version change in the library to be considered? Fine either way, but the 3rd party branches aren't maintained anymore so it seems whatever momentum this had has been lost for now. |
Apologies for not making a move on this, as you understand it's not a free change from the point of view of C++ users, and it hasn't been a big priority on my pile of work. I would happily assist to find ways to may this process painless, but in all likehood this is more like something we would adopt for 2.0. Technically the submitted PR #683 is probably not even a breaking change! (or if it is they are probably minor or fixables issues), the overheads I am trying to avoid are
There's an overlap with another "someday" change which would be to support wchar_t* all accross the board, because this is what Unreal uses naturally. So if we add some layer of abstraction for strings we might as well figure out a way to handle both cases. I generally feel we should be going in this direction but it's going to be a slow steering. I'll look into getting the PR resurrected for the latest version. |
(EDITED) |
+1 for this issue. Currently I have to check whether string_view is null terminated, and have to copy it to a pre-allocated buffer if it is not, which is a huge waste of performance if it is being done every frame. string_view is considered a standard way to pass immutable reference to string currently in modern C++. |
I discovered when trying to call ImGui from Rust that Rust strings are not null terminated and thus using there are some hurdles to jump through to produce a null terminated string to be use with ImGui.
I thought it would be convenient if I could use character data arrays instead of null terminated strings with ImGui.
I've made a branch with a proof-of-concept of this idea by replacing char* parameters in ImGui functions with ImStr - a struct containing a pointer to the start and the end of a string slice. It has a implicit char* constructor, so no calling code needs to be changed. I have a proof of concept branch at https://github.com/bitshifter/imgui/tree/imstr.
I haven't converted everything to ImStr, but it's enough to get the idea I think.
There may actually be some advantages in doing this for the C++ API:
Performance wise, it didn't seem to make a big difference in my profiling, I thought strlen might rise up in the profile but it didn't seem to for me at least.
If you are interested in this idea, I can try finish off converting the rest of the char* parameters a make a PR.
The text was updated successfully, but these errors were encountered: