ocornut/imgui

Auto Generated C Bindings

lmariscal opened this issue · 85 comments

Hi, I was wondering your perspective on what would be the ideal c bindings.
Something that resembles cimgui, the main issue with cimgui is that it is not able to compile with a pure C compiler, making a DynamicLibrary a most to be able to link at runtime. I just want to know your thoughts and find a way to help with this task.

Hello @cavariux,

I'm not a C user so I'm not sure I understand the constraint you mention with dynamic library being a must.

I think the cimgui base is fine + recent work on a generator e.g. #1782, but mostly this needs mostly to be streamlined, maintained and polished. Currently we often run into situation where people who are reliant on 2-levels of bindings (sometimes C++ > C > Some language) are forced to stay with old versions. As of today cimgui is on 1.53 only and Stephan appears to be busy to maintain it.

My vision is ideally we should have an official generator framework (in a format/language that most people can easily run). It would be able to emit the C bindings but also eventually be used to emit different bindings (e.g. people could just it to create the code/data required for other languages including custom languages). The code in #1782 is the closest we've got to that for C. It's not perfect, e.g. it doesn't emit a .h file that has the comments and formatting of the original imgui.h but can probably be a good base.

There's a similar situation with LUA bindings, there's at least two variants of generators with different features. The one in love-imgui had improvement pertaining to using multiple return values. And I hear users of LuaJIT + FFI may want a different solution that the raw LUA. Perhaps this could be handled by an officially maintained generator that could leverage the code/parsing used for a C generator?
https://github.com/patrickriordan/imgui_lua_bindings
https://github.com/slages/love-imgui/tree/master/

None of that is very hard problem to solve, but devil is in the details and most attempts are tackling the first 90% and never the remaining 90% because people are doing it for their own use. Perhaps having a clean, centralized solution for it would allow us to reach 100% and a situation where most bindings can be automated?

Omar

The gotcha there is that the header parser is not a generic one, but depends on the way I write the headers (so if you use a bunch of template stuff or even preparser things in a header, the parser wouldn't be able to figure it out).

Thanks Jari for this feedback (link https://github.com/jarikomppa/soloud/tree/master/scripts)
I think we'd be fine with that constraint too. It's much simpler to do a text-oriented parser with those constraints rather than an actual generic C++ parser.

Correct me if I am wrong, but I thing it is imposible to do a C binding to be compiled with a pure C compiler because the imgui part will always be C++ and has to be compiled also.
What cimgui does is just export the C interface (with extern C and __declspec(dllexport)) from a C++ compilation.

My proposal for a generic generator would be to parse imgui.h as done in my generator but instead of just generating the C binding, save also the parsed data (function name, return value, arguments and default values, structure it belongs to ...) in a Lua file that can then be used to generate various bindings.

Most of the pascal bindings was generated by scripts from heavily hand-edited source, which was a tedious step and it complicates updates a lot. If I could skip it, it would simplify things a lot.
I don't have a particular preference for format, if it can be easily dealt with.

As an example, Glad https://github.com/Dav1dde/glad generates opengl bindings from xml spec, maybe that can be an inspiration for the data structure (not format).

@ocornut I am now working on preserve comments in .h file
@jarikomppa I dont know json and my Lua work is already done. I would provide an example of how to use it for generating lua bindings so that, I expect, it could be easy to adapt to other languages

Better. I'm not the user but I think various things could be improved:

  • One-liner comments could be all aligned with each others. Various other alignments using spaces.
  • Comments that are not on function lines are missing.
  • Moving all typedefs at the top.
  • Spacing between each structures.
  • Member function in xxxx_xxxx formats could be changed to XxxxxXxxx to be consistent. They are only in this format when they are types matching std:: types.

Right now as is the file is quite unwelcoming to the user, we should aim toward something that looks very close to imgui.h.

One-liner comments could be all aligned with each others. Various other alignments using spaces.

the problem is generated in gcc -E -C -DIMGUI_DISABLE_OBSOLETE_FUNCTIONS ../../imgui/imgui.h output, as the multiple spaces are trimmed to one space

solution would be to avoid gcc parsing phase and parse imgui.h directly, but this parsing expands macros and defines so it is not desirable.

@ocornut the script that generates the c header and data, would you like to to be in this repo or on an external repo?

https://github.com/sonoro1234/cimgui/blob/master_auto2/cimgui/cimgui.h
now with typedefs at the begining and comments inside structs lie in a multiple of 10

@cavariux If it's good enough and it's not much of a maintenance/responsibility for me I think it could be in the main repo. Otherwise we could also perfectly create an official secondary repo for this script + all the additional "per-language" scripts. The location doesn't matter so much as long as it feels officially supported.

https://github.com/sonoro1234/cimgui/blob/master_auto2/cimgui/cimgui.h

now respects comments out code lines and also empty lines(for getting "Spacing between each structures")

I feel that respecting empty lines adds a lot of them that were generated in the gcc -E phase
What do you think about "respecting empty lines"?

I think ideally we shouldn't have a "gcc -E phase", especially as this create an external dependency to run the script.

Yes but it is a dependency that almost everybody has.
I can try to parse imgui.h directly but I dont know if it will be possible

I am surprised but it worked without gcc, perhaps because imgui.h is simple enough for that:

all code between #if and #endif is droped except for two typedefs: ImDrawIdx and ImTextureID
also had to set IMGUI_API to "" and thats all!!

The only caveat is that all the stuff generated by gcc preprocessor wont be automatically adapted by generator.lua from imgui.h but needs to be hardcoded in generator.lua

https://github.com/sonoro1234/cimgui/blob/master_auto2/cimgui/cimgui.h
was generated with scripts generatorNOGCC.bat and generatorNOGCC.lua

it works on imgui.dll compilation and in my lua binding generation

please review

Now I have updated not also cimgui (branch master_auto2) but also the automatic lua binding that can be used as a model for other bindings

this is the cimgui auto2 repo
https://github.com/sonoro1234/cimgui/tree/master_auto2/
where generator.bat or generatorNOGCC.bat (the first more robust as works with precompiled data, the second respects comments and indentation) gives us cimgui.cpp and cimgui.h that must be copied from generator to parent folder.

this is the luajit-imgui repo
https://github.com/sonoro1234/LuaJIT-ImGui
where lua/build.bat takes care of imgui.lua generation

to adapt to other bindings:
1- imgui_base.lua must be adapted to your language and ImGui_implementation. Only need to define ImVec2, ImVec4 and ImFontConfig and if you wish the opengl3 implementation
2- class_gen.lua must be adapted for generating the classes in your languaje

comments are welcome

@jarikomppa after some test lua table can be also saved in json format if this is better for anyone.

After making modifications mentioned in #1888 and #1887
I have been able to get two implementations (glfw and opengl3) which are the ones I can try from imgui/examples

I have used the gcc -E phase by the moment to avoid preprocesor complications

Here cimgui
https://github.com/sonoro1234/cimgui/tree/master_auto_implementations/cimgui
Here luajit-imgui
https://github.com/sonoro1234/LuaJIT-ImGui/tree/master_auto_implementations

I have added structs_and_enums.lua generation than althought is not necessary for LuaJIT ffi binding (which just use the C header) will be usefull for other bindings that need struct and enums information.

Until #1888 is not solved example/implementations are temporally disabled.
There is still my own implementation.

Implementations are enabled now and build samples provided in LuaJIT-ImGui.
Next incorporation will be json saving

Json saving incorporated

Some suggestions on the project setup:

  • Move the contents of cimgui/ to the root folder (and so cimgui/generator to generator)
  • Remove the .vscode folder.
  • Remove the .gif/.png from the repo (you can upload them to an issue to the wiki git repo if you need to link to them)
  • Is the GCC generator still used? If not remove the .bat file and rename the other one to generator.bat
  • Move tests if any to tests/ or generator/
  • The comments in both .bat files refer to non-existent files.

All except:
Remove the .gif/.png from the repo (you can upload them to an issue to the wiki git repo if you need to link to them)

Need to see one example for that.

As for the gcc, now it is used in generator_preprocess.bat which is more robust than without preprocess and could be reworked for using other compilers if someone helps with that
(basically provide preprocessor output as in gcc -E -C)

You can just drag .gif/.png file anywhere in any issue (even here) to get the file hosted by github. No need to get big files in a GIT repo. You can create a wiki on your repo (Github has the option) and add the files there, the Wiki is just a separate GIT repo.

I'll add a link to your repository if this isn't going to be merged in the existing known cimgui repository. Maybe see with extrawurst what plans you guys have.

I have tried to put image in the wiki but it asks for a link (and they suggest link to the repository!!)

I feel unsecure about generation without gcc because all I do is droping everything between #if.. and #endif
https://github.com/sonoro1234/cimgui/blob/91363f465a11c22342b979f92cc8054477ae1fea/generator/generator.lua#L83
and afterwards manually add some skiped definitions
https://github.com/sonoro1234/cimgui/blob/91363f465a11c22342b979f92cc8054477ae1fea/generator/generator.lua#L630
So the process is not completely automatic.

The solution adopted is generate cimgui.cpp and cimgui.h without gcc but also generate cimgui_auto.cpp and cimgui_auto.h (which remain in generated folder) if gcc is available

As for the gcc alternatives it seems that cl /E (MSVC) does the same as gcc -E
also clang -E
If anyone wants to try another compiler I could provide a script argument for that.

Another way could be to commit the generated code as part of the release process, then users will not need to use the generator scripts at all.

Another way could be to commit the generated code as part of the release process, then users will not need to use the generator scripts at all.

Yes. This is a no-brainer for me, it has to be done to reduce usage friction to the minimum!
Consider that some users of the C bindings won't be C users themselves so the process should be as seamless as possible for them.

Done

I have replaced cimgui files for the cimgui_auto (with preprocessor) files as the correct ones to use

#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
    // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now!
    // You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). See example applications if you are unsure of how to implement this.
    void        (*RenderDrawListsFn)(ImDrawData* data);
#else
    // This is only here to keep ImGuiIO the same size, so that IMGUI_DISABLE_OBSOLETE_FUNCTIONS can exceptionally be used outside of imconfig.h.
    void*       RenderDrawListsFnDummy;
#endif

this part of ImGuiIO succeds with gcc preprocessing and would succed even if RenderDrawListsFnDummy was not defined, but the not preprocessing strategy of dropping everything between #if and #endif fails by dropping both RenderDrawListsFn and RenderDrawListsFnDummy.

So this will be the setting for now.
The way to get better comments formating should have gcc preprocessing as the first stage and then try to do better comment formating based on this.
https://github.com/sonoro1234/cimgui/tree/Branch_v1.62.0

#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
    // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now!
    // You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). See example applications if you are unsure of how to implement this.
    void        (*RenderDrawListsFn)(ImDrawData* data);
#endif

Would be better because would only drop obsoletes.

Shouldn't you leave the #ifdef directives in the generated .c files (so, both blocks) and let the user define their own configuration?

In cimgui.cpp I should generate the function wrappers for obsolete functions but wrapped (only that ones) inside a #define (#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS)

Also
static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; }

Things like this are too hard for my limited parser.

@sonoro1234 I've been working on auto-generating my C# bindings based on your work. I think it's a fantastic step in the right direction, and I was able to get most of the "raw binding" layer done pretty quickly. Right now, I've just copy and pasted the "structs_and_enums.json" and "definitions.json" files into my project and am parsing through them manually. A few random comments from my initial work:

  • Figuring out the array sizes of fields is a little bit error-prone. Some fields have a direct array size, some have a size that is computed inline (e.g. "name": "InputCharacters[16+1]"), and some are based on an enumerated value. I'm handling all three cases right now, but it's a bit messy and requires me to pass around the set of enum definitions. I'm not sure how easy it would be, but it'd be nice if the json files included the final computed size.
  • Parsing through parameter types which are function pointers might be pretty annoying. I'm not handling it yet, so maybe it's easier than I'm anticipating.
  • igSliderAngle is missing one of its default values (v_degrees_max):
    "argsoriginal": "(const char* label,float* v_rad,float v_degrees_min=-360.0f,float v_degrees_max=+360.0f)",
    "defaults": { "v_degrees_min": "-360.0f" },

Other than that, it has been pretty smooth. I would love to switch over to generating my bindings entirely, because it will cut out a big chunk of busy work when updating. My WIP is here, but it's not in a usable state yet.

A few more notes / questions:

  • Are typedefs discoverable? I've hardcoded some of these for now, but there's quite a few.
  • Some structs are forward-declared and only referenced with a pointer. I've just hard-coded these as opaque pointers for now, but maybe this should be discoverable somehow.
  • ImVector is empty -- I'm guessing this is because it's templated. I'll likely provide my own custom wrapper for ImVector to make it more usable, anyways.
  • Unions are a bit broken, e.g.
        "Pair": [
            {
                "type": "ImGuiID",
                "name": "key"
            },
            {
                "type": "union { int val_i; float val_f; void* val_p;",
                "name": "}"
            }
        ],

Again, I'll just provide a custom wrapper for that, most likely. This is the only union that I see, so it's probably not worth caring too much about.

  • "inline" and "const" are in the type signatures, but they aren't really meaningful -- I think most people will end up filtering them out (?)
  • Some functions taking ImVector have a weirdly-formatted parameter, e.g.:
    "TextRange_split": [
        {
            "funcname": "split",
            "args": "(char separator,ImVector_TextRange out)",

I don't think I care about any functions that want you to pass around ImVectors, though -- seems like that is probably internal stuff mostly, that not many will be interested in calling.

@mellinoe
I am glad that you found some use for the generator.

Figuring out the array sizes of fields is a little bit error-prone. Some fields have a direct array size, some have a size that is computed inline (e.g. "name": "InputCharacters[16+1]"), and some are based on an enumerated value. I'm handling all three cases right now, but it's a bit messy and requires me to pass around the set of enum definitions. I'm not sure how easy it would be, but it'd be nice if the json files included the final computed size.

This is verbatim copy of imgui.h: https://github.com/ocornut/imgui/blob/master/imgui.h#L1131
which means just C definitions.
For my binding this is not a problem because LuaJIT compiles C code.
In your binding you should parse this three cases you mentioned. I find dificult to fo follow the code, perhaps is https://github.com/mellinoe/ImGui.NET/blob/autogen/src/CodeGenerator/Program.cs#L276
where you sanitize?

(I will review the other points you mention also)

igSliderAngle
This is in fact bad defined. I will take a look.

Are typedefs discoverable? I've hardcoded some of these for now, but there's quite a few.
I dont understand your needs. Perhaps if I see those hardcoded typedefs I could help.

(Perhaps https://github.com/Extrawurst/cimgui/issues would be the best place for sending issues one by one.)

I dont understand your needs. Perhaps if I see those hardcoded typedefs I could help.

An an example of what I mean:

        "ImFontAtlas": [
            {
                "type": "ImTextureID",
                "name": "TexID"
            },

ImTextureID isn't a type that is referenced in the json files -- it's a typedef for void*, but you'd need to inspect the source code to learn that. Similarly, there are forward-declared structures that are used but not defined in the json files.

To circle back, the above is only an issue if the goal is to be able to generate bindings with only the json files. It's entirely possible that I should just continue looking at the header files (or parsing them myself) for that information.

(igSliderAngle issue is solved now and also igPlotLines and igPlotHistogram)

In typedefs are you needing?

        "ImFontAtlas": [
            {
                "type": "void *",
                "name": "TexID"
            },

ImVector is left out because it is templated. One solution would be to generate several types (one for each template argument)
Unions are broken but dont know how to solve it with name and type.
const has a meaning for LuaJIT that knows it wont be modified.
inline has no meaning in fact (I can drop it if it is anoying)
What is weirdly-formatted in TextRange_split?

In typedefs are you needing?

That would be fine for my uses, I think.

ImVector is left out because it is templated

I think it's fine either way. I think ImVector is special enough that you will want to write "special" bindings for it anyways, by hand. That's what I think I will end up doing.

Unions are broken but dont know how to solve it with name and type.

Yeah, I'm not sure, either. Since there is only one (that I know if), it's not a big deal.

What is weirdly-formatted in TextRange_split?

Just the ImVector_TextRange part.

ImVector_TextRange is a typedef for ImVector in C++ but for ImVector in C (with void* Data)

struct ImVector
{
    int Size;
    int Capacity;
    void* Data;
};

For typedefs I could provide a dictionary as key:"ImTextureID" value:"void *"

Just pushed a typedefs_dict
If you are missing some typedef please ask for it.

ImVector_TextRange is passed by reference in imgui. Should be a pointer in C in order to be able to modify it inside the function. I dont use it so I didn't noticed. I should check all arguments passed by reference.
Most of them as const ImVec2& uv0 cant be changed so it is enough to pass by value.

I could change this prototype to use a pointer. Also maybe TextRange should later be extracted into something else (it's interesting a ranged string view).
EDIT Done. Also clarified stuff that meant to be internals so they don't get relied upon.

@ocornut
The same happens in ColorConvertRGBtoHSV and ColorConvertHSVtoRGB.
If you change all the refs that are not const for pointers it will be ok. If not I would track all references that are not const and use a pointer in the C function that will be dereferenced as *pt in the C++ function which means a rework of what I call call_args (see below)

@mellinoe
Parsing through parameter types which are function pointers might be pretty annoying. I'm not handling it yet, so maybe it's easier than I'm anticipating.

For example in PlotLines you have args "(const char* label,float(values_getter)(void data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size)" for C function definition
but also call_args "(label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size)"
for function calling

It is used in https://github.com/sonoro1234/LuaJIT-ImGui/blob/master_auto_implementations/lua/class_gen.lua#L82 for generating struct_functions in LuaJIT

As for parameters that are function pointers, I'll show an example so it's more clear what I mean. Here's what I need to generate for a C# definition for igPlotLinesFnPtr:

public delegate float ValueGetterFn(void* data, int idx);

[DllImport("cimgui")]
public static extern void igPlotLinesFnPtr(
    byte* label,
    ValueGetterFn values_getter,
    void* data,
    int values_count,
    int values_offset,
    byte* overlay_text,
    float scale_min,
    float scale_max,
    Vector2 graph_size);

To figure out the signature of the function pointer type, I'll need to parse through the argument string, match parentheses and commas, etc. It's doable, just a little tricky. Is it possible to generate such a list in a similar way to call_args?

The main problem is how to present data.
This is the definition in Lua of igPlotLinesFnPtr

defs["igPlotLines"][2] = {}
defs["igPlotLines"][2]["funcname"] = "PlotLines"
defs["igPlotLines"][2]["args"] = "(const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size)"
defs["igPlotLines"][2]["ret"] = "void"
defs["igPlotLines"][2]["call_args"] = "(label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size)"
defs["igPlotLines"][2]["argsoriginal"] = "(const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset=0,const char* overlay_text=((void *)0),float scale_min=3.40282346638528859812e+38F,float scale_max=3.40282346638528859812e+38F,ImVec2 graph_size=ImVec2(0,0))"
defs["igPlotLines"][2]["stname"] = "ImGui"
defs["igPlotLines"][2]["comment"] = ""
defs["igPlotLines"][2]["ov_cimguiname"] = "igPlotLinesFnPtr"
defs["igPlotLines"][2]["defaults"] = {}
defs["igPlotLines"][2]["defaults"]["overlay_text"] = "((void *)0)"
defs["igPlotLines"][2]["defaults"]["values_offset"] = "0"
defs["igPlotLines"][2]["defaults"]["graph_size"] = "ImVec2(0,0)"
defs["igPlotLines"][2]["signature"] = "(const char*,float(*)(void*,int),void*,int,int,const char*,float,float,ImVec2)"
defs["igPlotLines"][2]["cimguiname"] = "igPlotLines"

So the signature of values_getter is (void* data,int idx) and the return value is float.
Where should this be shown?

We could have an args array with argname and argtype:

1 label, const char*
2 values_getter, ,float(*)(void*,int)
3 data, void *
4 etc

But it is not exactly what you mean.
Perhaps when it is a function type we could provide extra fields: ret "float" and signature "(void*,int)"
But some extra parsing is not avoidable as it depends on the target language of the binding.
@mellinoe is that solution useful for you?

@mellinoe Have just pushed the args array described above.

Pending to solve ColorConvertRGBtoHSV and ColorConvertHSVtoRGB and TextRange_split references issue until @ocornut response.

We have a small issue with ColorConvertRGBtoHSV/etc. because they are public API, whereas TextRange::split wasn't meant to (and it now patched anyway). generally speaking I think all the color helpers are currently a mess and need a big pass of redesign. It would feels inadequate to break their signature today if what they need is a larger amount of redesign down the line.

My suggestion would be that it is OK to omit those 2 functions from bindings if they are problematic to you, since they are merely helper and C#/Lua ecosystem are likely to provide better alternatives.

@mellinoe Modified structs_and_enums.json: added size to struct members (with calculated effective number) and calc_value to enums.

I think everything is solved, waiting for your feedback.

@mellinoe In case you find it useful, this is a list of all default values

 ["-360.0f"]=true
 ["\"\""]=true
 ["false"]=true
 ["100"]=true
 ["-1"]=true
 ["0.5f"]=true
 ["255"]=true
 ["-1.0f"]=true
 ["\"%.6f\""]=true
 ["12"]=true
 ["10"]=true
 ["+360.0f"]=true
 ["ImVec2(-1,0)"]=true
 ["ImDrawCornerFlags_All"]=true
 ["\"Filter(inc,-exc)\""]=true
 ["0"]=true
 ["ImVec2(0,1)"]=true
 ["ImVec2(1,0)"]=true
 ["1"]=true
 ["\"%d\""]=true
 ["0xFFFFFFFF"]=true
 ["ImVec2(0,0)"]=true
 ["1.0f"]=true
 ["3.40282346638528859812e+38F"]=true
 ["\"%.3f\""]=true
 ["ImVec2(1,1)"]=true
 ["0.0f"]=true
 ["sizeof(float)"]=true
 ["true"]=true
 ["ImVec4(1,1,1,1)"]=true
 ["ImVec4(0,0,0,0)"]=true
 ["((void *)0)"]=true}

I picked up where I left off and got quite a bit further on my branch -- it runs the basic demo now with the autogenerated bindings. It's still pretty rough, and I'm planning to add more of a convenience layer, but for now it is functional. A couple things I hit:

  • Building on Windows with make+gcc was a little painful, so I hacked up a vcxproj and used that to test with. Something is misconfigured, though, so I'm getting some warnings / errors with certain functions. Once I iron those issues out, would you take a PR for such a project ifle?

  • Some auto-generated methods aren't returning anything. For example:

CIMGUI_API bool igTreeNodeExPtr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,...)
{
    va_list args;
    va_start(args, fmt);
    ImGui::TreeNodeExV(ptr_id,flags,fmt,args);
    va_end(args);
}

It affects:

  • igTreeNodeStrStr
  • igTreeNodePtr
  • igTreeNodeExStrStr
  • igTreeNodeExPtr

@sonoro1234 Thanks a lot for all your work on this, I think it's going to be a great help once the transition finishes.

@mellinoe
Just solved return issue.
Yes I would take a vcxproj PR.
Why was make+gcc painful?
see https://github.com/sonoro1234/LuaJIT-ImGui/blob/master_auto_implementations/CMakeLists.txt
A CMakeList.txt could be provided to generate any compiler project, but should it add implementations or whatever?

CMake support would also be a good alternative. Personally, I'm a lot more familiar with CMake and am already set up on all my machines to use it.

should it add implementations or whatever?

If you're referring to the SDL2/GLFW/OpenGL implementations, then I would say no.

CMakeLists.txt already pushed

Also fixed Makefile so that mingw32-make in cimgui directory does the work on windows

Thanks, the CMakeLists file is very helpful. When I build the project in a 64-bit configuration, I get these warnings, which I'm not familiar with. I was also getting these same warnings with the project I manually created, but the one generated via CMake also has them. As far as I can tell, they have something to do with the constructor definition for ImVec2.

2>cimgui_auto.h(836): warning C4190: 'igGetWindowPos' has C-linkage specified, but returns UDT 'ImVec2' which is incompatible with C
2>imgui.h(126): note: see declaration of 'ImVec2'
2>cimgui_auto.h(837): warning C4190: 'igGetWindowSize' has C-linkage specified, but returns UDT 'ImVec2' which is incompatible with C
2>imgui.h(126): note: see declaration of 'ImVec2'

etc.

When I call any of these functions, I get an access violation. Any idea what's causing that?

After thinking about it, it occurs to me that the functions should return their result through a pointer, as "mainline" cimgui does:

CIMGUI_API void igGetMousePos(struct ImVec2 *pOut);

If that is necessary, perhaps the json file can describe this transformation somehow. E.g. it could contain:

  "igGetMousePos": [
    {
      "funcname": "GetMousePos",
      "ret": "void",
      "retParam": "pOut", // Indicates that the "pOut" parameter should be treated as the return value.
      // Other stuff

Which would let the binding layers switch the parameter back to a return value.

This is an important decission.
I am not getting any error with mingw`s gcc 32 bits.
I must google-investigate this. (until now all references found point to MSVC)

@mellinoe @sonoro1234

I think it is reasonable to return those small structures as value, especially considering ImVec2, the most commonly used structure, is 8 bytes. It would be worth investigating the issue instead of writing back through the pointer argument.

@mellinoe

When I call any of these functions, I get an access violation. Any idea what's causing that?

Is access violation happening when you use the ImVec2 returned value or just by calling igGetMousePos?
In the first case how did you use ImVec2?
In the second one: Did you saw the exported function using dependency walker?

Also: Did you compile the cimgui DLL and the DLL client with the same compiler?
Did you get this behaviour with 32 bits?
Does it happen with the /clr option of msvc compiler?

Also:
In your autogenerated bindings calling convention is not specified (while it is in the manual ones)
Could be C# issue: How is ImVec2 translated to Vector2?
In

public static Vector2 GetMousePos()
        {
            Vector2 ret = ImGuiNative.igGetMousePos();
            return ret;
        }

Can you somehow check what ImGuiNative.igGetMousePos(); is returning?

Also:
How is it that ImVec2 can be used in igSetNextWindowPos(const ImVec2 pos,ImGuiCond cond,const ImVec2 pivot) as an struct argument given by value but it can`t be returned by value?

Also:
adding this function to cimgui_auto.cpp

CIMGUI_API int GetImVec2Size()
{
	ImVec2 aa;
	return sizeof(aa);
}

I am getting 8 as return (and 16 with ImVec4) Could you check that?

I can have both functions as

defs["igGetMousePos"][2]["funcname"] = "GetMousePos"
defs["igGetMousePos"][2]["args"] = "(ImVec2 *pOut)"
defs["igGetMousePos"][1]["ret"] = "void"
defs["igGetMousePos"][1]["comment"] = ""
defs["igGetMousePos"][1]["call_args"] = "(pOut)"
defs["igGetMousePos"][1]["argsoriginal"] = "(ImVec2 *pOut)"
defs["igGetMousePos"][1]["stname"] = "ImGui"
defs["igGetMousePos"][1]["ov_cimguiname"] = "igGetMousePos_notUDT"
defs["igGetMousePos"][1]["cimguiname"] = "igGetMousePos"

where nonUDT in ov_cimguiname tell us the situation.
Both will be compiled (one with warnings) and any of them can be used.

But I am still very interested about the questions above. Please answer as much as you are able to.

Is access violation happening when you use the ImVec2 returned value or just by calling igGetMousePos?
In the first case how did you use ImVec2?
In the second one: Did you saw the exported function using dependency walker?

The AV is happening inside igGetMousePos. I can see that the function is being exported as expected.

Also: Did you compile the cimgui DLL and the DLL client with the same compiler?

I'm loading cimgui.dll from C#.

Did you get this behaviour with 32 bits?

I haven't tried it yet, but it's a good idea. I will try to do that tomorrow.

In your autogenerated bindings calling convention is not specified (while it is in the manual ones)

Yeah, that's a good catch. It doesn't make a difference here, unfortunately. It fails either way.

Could be C# issue: How is ImVec2 translated to Vector2?

Nothing special is done for ImVec2/Vector2, since it's a simple blittable struct with an identical format on both sides.

How is it that ImVec2 can be used in igSetNextWindowPos(const ImVec2 pos,ImGuiCond cond,const ImVec2 pivot) as an struct argument given by value but it can`t be returned by value?

The problem seems to be specific to return types. By the way, the warning is also generated for ImColor ImColor_HSV(), which is the only other return type that is not either a pointer, or a basic numeric type. If I change a function to return ImVec4, the warning also appears there.

Interestingly, calling ImColor_HSV does not crash for me, and it seemingly works okay (although I didn't check the math).

Does it happen with the /clr option of msvc compiler?
Could you please try GetImVec2Size()?

Support for UDT returning functions is done!! (ImVec2, ImVec4, ImColor)
nonUDT field is true for them

I still get UDT warnings warnings when compiling the library?

UDT warnings are expected (in MSVC) but not harmful. You can still try to use this functions, but with MSVC (is this your compiler?) use better the ones with name ended with _nonUDT.

Exploring a bit more tonight. Like I mentioned earlier, I think these UDT warnings can be avoided if the return types are plain structs without any constructors/operators/functions/etc. For example, if I change igGetMousePos to this:

struct ImVec2_Simple { float x; float y; };
CIMGUI_API ImVec2_Simple igGetMousePos()
{
    ImVec2 pos = ImGui::GetMousePos();
    ImVec2_Simple result;
    result.x = pos.x;
    result.y = pos.y;
    return result;
}

I'm able to build without any warnings, and I'm able to call this function successfully from C#. Not sure if there's some way we can easily accomplish this for all of the affected functions without generating a bunch of wrapper code like the above. Some way to conditionally define the structs so that they are POD types for the exported C functions?

To the function_nonUDT already existent I have added function_nonUDT2 which returns simple types for ImVec2, ImVec4 and ImColor.

In function definitions nonUDT field is 1 for _nonUDT (pointer in args) or 2 for _nonUDT2 returning UDT_Simple

@pragmascript has pointed out that the ImGuiMouseCursor enum has some values that are off by one. Starting at ImGuiMouseCursor_TextInput, the values one higher than the native version.

https://raw.githubusercontent.com/sonoro1234/cimgui/latest_cimgui_auto/generator/generated/structs_and_enums.json

imgui/imgui.h

Line 1005 in 781a795

ImGuiMouseCursor_None = -1,

enum ImGuiMouseCursor_
{
    ImGuiMouseCursor_None = -1,
    ImGuiMouseCursor_Arrow = 0,
    ImGuiMouseCursor_TextInput,         // When hovering over InputText, etc.
    ImGuiMouseCursor_ResizeAll,         // (Unused by imgui functions)
    ImGuiMouseCursor_ResizeNS,          // When hovering over an horizontal border
    ImGuiMouseCursor_ResizeEW,          // When hovering over a vertical border or a column
    ImGuiMouseCursor_ResizeNESW,        // When hovering over the bottom-left corner of a window
    ImGuiMouseCursor_ResizeNWSE,        // When hovering over the bottom-right corner of a window
    ImGuiMouseCursor_Hand,              // (Unused by imgui functions. Use for e.g. hyperlinks)
    ImGuiMouseCursor_COUNT

They should be -1,0,1,2,3...?
Just pushed.

@sonoro1234 Yep, that's exactly it. Thanks for the quick fix!

Excellent! What is the state and it and usability for e.g. ImGui.Net?
Should I start replacing links to point primarily to this version?

(PS: Small suggestion, don't store images in the git repo, instead you can upload to issue and link to those images, or upload on the wiki which itself is another git repo. Also updating that oooooold screenshot would be nice)

@mellinoe should answer about usability in ImGui.Net
As for LuaJIT-imgui everything works

Yes, you could link to cimgui/cimgui as it is the same as sonoro1234/cimgui now.

I will take care of screenshot also ;-)

I'm still working on polishing up the autogen branch of ImGui.NET, but as far as I can tell, the current state of cimgui is good for my purposes. My branch already includes function imports for everything that cimgui exports, but those are just the direct, unsafe functions. They aren't very convenient to use directly because they involve pointers, etc. I'm currently working on generating a safe wrapper layer with some convenience features (e.g. the ability to use a C# String and wrapping raw pointers in safe helper types).

Closing this as this work by @sonoro1234 is now released and available at
https://github.com/cimgui/cimgui

Various links updated to point to this repository.
In the next imgui release notes I will point to this new repo and the news that cimgui is back on track :)

Sorry for bumping such an old issue, but the user-defined data types thing became an issue again for consumers via interop with the new docking branch now that ImGui has a bunch of callbacks for us to implement now.

I dug into this more and found out exactly why it's an issue (on Windows, anyway.)


Disclaimers up front:

  • Everything I've looked into applies on x64/x86 Windows with the MSVC compiler. (I suspect it applies to other compilers on Windows as well since they probably use Windows calling conventions.)
  • This sadly does not apply to x64 Linux with GCC. It returns ImVec2 via register like you'd expect.
    • I imagine the same for macOS, but I didn't have time to test.
  • I can't find official guidance on how returning user-defined types works with __cdecl or __stdcall.

I've also uploaded a repository with non-ImGui test code for .NET Core on both Windows and Linux.


I think it is reasonable to return those small structures as value, especially considering ImVec2, the most commonly used structure, is 8 bytes. It would be worth investigating the issue instead of writing back through the pointer argument.

This is actually the root of the issue. On x64 Windows, user-defined types are only returned by value in very specific circumstances, and ImGui is not meeting those circumstances. (The specific requirements are outlined here, TL;DR: Anything with constructors, etc are not returned via register.)

On x64 Windows (and seemingly, x86 Windows), a function like this:

ImVec2 GetWindowPos(ImGuiViewport* viewport)
{
    printf("GetWindowPos(%d)\n", viewport->ID);
    return ImVec2(1879.f, 3226.f);
}

is effectively rewritten to look like this:

ImVec2* GetWindowPos(ImVec2* ret, ImGuiViewport* viewport)
{
    printf("GetWindowPos(%d)\n", viewport->ID);
    *ret = ImVec2(1879.f, 3226.f);
    return ret;
}

So as an example in C# interop land, this is how you can install the Platform_GetWindowPos callback, but only on Windows:

using ImGuiNET;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

public class ImGuiSupport
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private unsafe delegate void* PlatformGetWindowPositionDelegate(out Vector2 vector, ImGuiViewportPtr viewport);

    // Delegates must be cached to ensure they aren't garbage collected while ImGui holds a pointer to them.
    private readonly List<Delegate> MarshaledDelegateCache = new List<Delegate>();

    public ImGuiSupport()
    {
        PlatformGetWindowPositionDelegate platformGetWindowPosition = PlatformGetWindowPosition;
        MarshaledDelegateCache.Add(platformGetWindowPosition);
        ImGui.GetPlatformIO().Platform_GetWindowPos = Marshal.GetFunctionPointerForDelegate(platformGetWindowPosition);
    }

    private unsafe void* PlatformGetWindowPosition(out Vector2 ret, ImGuiViewportPtr viewport)
    {
        Console.WriteLine($"Getting window position for {(ulong)Unsafe.As<ImGuiViewportPtr, IntPtr>(ref viewport):X}");
        ret = new Vector2(1879f, 3226f);
        return Unsafe.AsPointer(ref ret);
    }
}

@ocornut Since the docking branch is still young, can we change the callbacks to return their ImVec2 results by pointer instead? From C# at least, it makes interop much simpler than trying to deal with this differently for each platform.

I can make a PR if you're OK with the change.

Another viable alternative would be to make the non-data members of ImVector2 and friends optional, but this would have a more intrusive impact on the ImGui codebase since it uses them.

@PathogenDavid There is quite a bit more discussion about the viewport branch's new "Platform" function pointers over in the cimgui repo: cimgui/cimgui#71 (comment). The solution to that ABI mismatch is a little bit onerous, but nothing about it should require platform-specific or compiler-specific code. It basically just involves creating a native "thunk" that shuffles around the parameter/return value from the user-provided callback. I've tested the docking branch in ImGui.NET with that workaround and it works, although I haven't pushed it anywhere yet. It should work on any platform or compiler.

EDIT: Somehow it slipped my mind that the "nonUDT2" overloads are already a part of cimgui for some time, and they have normal C-compatible return values like the suggestion above. Disregard this second part.