Skip to content

Conversation

@mikomikotaishi
Copy link

This PR creates a C++ module for Dear ImGui, rather than using generated bindings (as I have seen some other PRs propose), which is simpler for most users. The README file is updated as well to mention this.

@ocornut
Copy link
Owner

ocornut commented Nov 26, 2025

Hello,
Thanks for your PR.
Could you clarify the difference with #8868 / https://github.com/stripe2933/imgui-module/ ?
cc @stripe2933

If it is a discoverability issue, I am happy to refer to this from more location (it's already in misc/cpp/) but I am not sure I want to take on maintenance of this now, considering I have no idea how C++ modules work nor of their potential inevitable drawback.

I am happy to offer hosting @stripe2933 solution as part of https://github.com/dearimgui/ if you like.

@stripe2933
Copy link
Contributor

stripe2933 commented Nov 26, 2025

I am happy to offer hosting @stripe2933 solution as part of https://github.com/dearimgui/ if you like.

Yes, that would be appreciated.
Additionally, I will consider adding imgui to https://arewemodulesyet.org/.


Edit: relevant PR can be found in kelteseth/arewemodulesyet#71

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Nov 26, 2025

@ocornut From an API point of view having a static module file is easier to maintain than dynamically generating the module from a script, especially if it is C++ and not another language. I don't see the point of the added complexity of using a script, which has a greater reliability risk than manually producing the module). (If you look at other libraries with modules, basically all of them provide them as static files rather than a generated file.)

If you are concerned about maintaining the symbols in two different files, the way it works is whenever you add a new function you can just write the using statement to re-export it from the module, as there is a using statement for each symbol in the library.

@ocornut
Copy link
Owner

ocornut commented Nov 26, 2025

I kinda agree with this sentiment, and I suppose the worse case if that if I forget something it may be caught up with.

Mainly at this moment I am not ready to accept maintaining this myself, because I don't really know/understand what it would really entail in term of user support. As a rule of thumb, I am expecting that anything designed by the C++ committee is going to backfire at some point in time, so I would prefer if this was independently maintained first.

Would you like to host your own copy in a public repository and consider maintaining it first?
I would happily increase visibility to this (already added two new links to the existing imgui-module).

@ocornut
Copy link
Owner

ocornut commented Nov 26, 2025

There's a big difference between your implementation and stripe2933's one: the later is including all individual flags/enums in the .ccpm file. Therefore it is bigger and I believe it justify being automated.

Yours:

export {
    using ::ImGuiWindowFlags;
[...]
export {
    using ::ImGuiWindowFlags_;

stripe2933's one:

    using ::ImGuiWindowFlags_;
    using ::ImGuiWindowFlags_None;
    using ::ImGuiWindowFlags_NoTitleBar;
    using ::ImGuiWindowFlags_NoResize;
    using ::ImGuiWindowFlags_NoMove;
    using ::ImGuiWindowFlags_NoScrollbar;
    using ::ImGuiWindowFlags_NoScrollWithMouse;
    using ::ImGuiWindowFlags_NoCollapse;
    using ::ImGuiWindowFlags_AlwaysAutoResize;
    using ::ImGuiWindowFlags_NoBackground;
    using ::ImGuiWindowFlags_NoSavedSettings;
    using ::ImGuiWindowFlags_NoMouseInputs;
    using ::ImGuiWindowFlags_MenuBar;
    using ::ImGuiWindowFlags_HorizontalScrollbar;
    using ::ImGuiWindowFlags_NoFocusOnAppearing;
    using ::ImGuiWindowFlags_NoBringToFrontOnFocus;
    using ::ImGuiWindowFlags_AlwaysVerticalScrollbar;
    using ::ImGuiWindowFlags_AlwaysHorizontalScrollbar;
    using ::ImGuiWindowFlags_NoNavInputs;
    using ::ImGuiWindowFlags_NoNavFocus;
    using ::ImGuiWindowFlags_UnsavedDocument;
    using ::ImGuiWindowFlags_NoDocking;
    using ::ImGuiWindowFlags_NoNav;
    using ::ImGuiWindowFlags_NoDecoration;
    using ::ImGuiWindowFlags_NoInputs;
    using ::ImGuiWindowFlags_DockNodeHost;
    using ::ImGuiWindowFlags_ChildWindow;
    using ::ImGuiWindowFlags_Tooltip;
    using ::ImGuiWindowFlags_Popup;
    using ::ImGuiWindowFlags_Modal;
    using ::ImGuiWindowFlags_ChildMenu;

using ::ImGuiWindowFlags;

Can you explain is the later is actually justified/required to create a module file?

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Nov 26, 2025

Those are the unscoped enum constants that are automatically all exported by just exporting the enum itself. We don't actually have to export those manually

I could run it as an independent but still related repo for the time being and update it as necessary, for ease of building I considered adding CMake support because this is to my knowledge the simplest and most ubiquitous way to compile modular translation units but I don't want to add things that would otherwise bloat or break the philosophy of the library.

@stripe2933
Copy link
Contributor

stripe2933 commented Nov 27, 2025

Those are the unscoped enum constants that are automatically all exported by just exporting the enum itself. We don't actually have to export those manually

Based on my test, it works with Clang but not with MSVC.

C:\PROGRA~1\MICROS~2\2022\ENTERP~1\VC\Tools\MSVC\1444~1.352\bin\Hostx64\x64\cl.exe  /nologo /TP -DBOOST_ALL_DYN_LINK=1 -DBOOST_CONTAINER_DYN_LINK -DBOOST_CONTAINER_NO_LIB -DCGAL_USE_GMPXX=1 -DEXACT_BOUNDING_VOLUME_USING_CGAL -DGLFW_DLL -DGLFW_INCLUDE_NONE -DIMATH_DLL -DMESHOPTIMIZER_ALLOC_EXPORT -DMESHOPTIMIZER_API=__declspec(dllimport) -DNFD_SHARED -DOPENEXR_DLL -DSUPPORT_EXR_SKYBOX -DSUPPORT_KHR_TEXTURE_BASISU -DVULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 -ID:\a\vk-gltf-viewer\vk-gltf-viewer\extlibs\include -ID:\a\vk-gltf-viewer\vk-gltf-viewer\include -ID:\a\vk-gltf-viewer\vk-gltf-viewer\build\shader -ID:\a\vk-gltf-viewer\vk-gltf-viewer\build\bloom\shader -ID:\a\vk-gltf-viewer\vk-gltf-viewer\build\cubemap\shader -ID:\a\vk-gltf-viewer\vk-gltf-viewer\build\ibl\shader -external:ID:\a\vk-gltf-viewer\vk-gltf-viewer\build\vcpkg_installed\x64-windows-release\include -external:ID:\a\vk-gltf-viewer\vk-gltf-viewer\VULKAN_SDK\include -external:ID:\a\vk-gltf-viewer\vk-gltf-viewer\build\vcpkg_installed\x64-windows-release\incl
table.cppm
D:\a\vk-gltf-viewer\vk-gltf-viewer\interface\helpers\imgui\table.cppm(49): error C3861: 'ImGuiTableColumnFlags_WidthFixed': identifier not found
D:\a\vk-gltf-viewer\vk-gltf-viewer\interface\helpers\imgui\table.cppm(49): error C2065: 'ImGuiTableColumnFlags_WidthFixed': undeclared identifier
D:\a\vk-gltf-viewer\vk-gltf-viewer\interface\helpers\imgui\table.cppm(69): error C3861: 'ImGuiTableColumnFlags_WidthFixed': identifier not found
D:\a\vk-gltf-viewer\vk-gltf-viewer\interface\helpers\imgui\table.cppm(69): error C2065: 'ImGuiTableColumnFlags_WidthFixed': undeclared identifier

I’m not fully aware of the exact C++ standard rules behind this, but to maximize compiler compatibility, using automated bindings seems to be the better approach. I will check GCC’s behavior later and update accordingly.

@mikomikotaishi
Copy link
Author

Then why not just offer the modules as you have already provided here? This is far less overhead than dynamically generating the module each time. This seems to make the most sense to me.

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Nov 27, 2025

I applied the same changes you made on your own repository, but with the following notes:

  • Modules do not (and should not) need to export the internals of a library. Modules are after all, designed to preserve encapsulation.
  • Modules do not need to export the backend of the library, including the headers for GLFW or Vulkan, etc. There is essentially no reason for this as the actual library usage is defined by the public API, not its backend.
  • I'm keeping the module name ImGui (rather than imgui) so that it aligns with the namespace. It's best if we keep module names and namespace names identical.
    At least in the end, providing the module directly is the most stable option and easiest for consumers to read the module API with, rather than having it generated through a script. It is also better for a human to choose which symbols should be exported rather than just exporting all of the symbols, especially when the goal is to preserve encapsulation and not flood the file with internal symbols when a user is primarily interested in only public symbols.

@stripe2933
Copy link
Contributor

Then why not just offer the modules as you have already provided here? This is far less overhead than dynamically generating the module each time. This seems to make the most sense to me.

I can't understand what you're saying. The overall workflow is:

  1. https://github.com/stripe2933/imgui-module regularly checks the release of ImGui, and release the generated binding when it released. It is done only once.
  2. Whenever the user want to update their ImGui version in the project, they also download the matching version of binding. The file you mentioned is downloaded from here.

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Nov 27, 2025

I mean generating the module for each user, as opposed to simply providing a static module as a "what-you-see-is-what-you-get". Also, the overhead of the author simply updating the list of exported symbols each update is much less than that of maintaining a Python script as well as finding bugs whenever they inevitably occur.

@stripe2933
Copy link
Contributor

Modules do not (and should not) need to export the internals of a library. Modules are after all, designed to preserve encapsulation.

Using imgui_internal module is optional. But in my experience, still many useful ImGui features are in imgui_internal.h file, and sophisticated application wants to tweak it.

Modules do not need to export the backend of the library, including the headers for GLFW or Vulkan, etc. There is essentially no reason for this as the actual library usage is defined by the public API, not its backend.

Then how the application can initialize the backend code?

I'm keeping the module name ImGui (rather than imgui) so that it aligns with the namespace. It's best if we keep module names and namespace names identical.

Module names and namespaces are orthogonal. Whether to make them the same is a matter of personal choice.

At least in the end, providing the module directly is the most stable option and easiest for consumers to read the module API with, rather than having it generated through a script. It is also better for a human to choose which symbols should be exported rather than just exporting all of the symbols, especially when the goal is to preserve encapsulation and not flood the file with internal symbols when a user is primarily interested in only public symbols.

I agree with your points. By explicitly adding the export keyword to the symbols within the library, the library author can precisely control which symbols are exported. This also reduces the likelihood of errors that can occur when generating separate binding files. In fact, Vulkan-Hpp once released source code that failed to compile due to a typo in the module binding file, and my binding generator also handles certain symbols heuristically due to limitations in cimgui (though I mitigate this by using additional build CI to prevent such issues from reaching releases).

However, since the imgui library author has mentioned that he is not fully comfortable with C++ modules yet and maintaining them in this state would be difficult, the best approach for us is to host the symbol-export file in an external repository, encourage many users to rely on it, and work toward stabilizing it so that it can eventually be included in the official repository.

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Nov 27, 2025

But in my experience, still many useful ImGui features are in imgui_internal.h file, and sophisticated application wants to tweak it.

A module is designed to encapsulate the public API of the library, if they want to change it they can always #include the internals header and create a new module to create whatever utilities or extensions they want. It's not the library's responsibility to create a public interface for internals, and that is extraneous work which if the library maintains at that point ought to just be an official API, and even including another header doesn't incur a compile-time penalty that would be any greater than compiling another module that includes that same header anyway.

Then how the application can initialize the backend code?

Again, are these parts of the library API? Implementation details aren't the responsibility of a library to make public, unless it decides these are public API that need to be maintained/documented to the same degree. If a consumer really needs to access the backend and insist on doing so through modules, they can do the exact same thing mentioned earlier of creating their own module to access said features, but those if those features were intended to be public they would not have been relegated to a file outside of the main header.

Module names and namespaces are orthogonal. Whether to make them the same is a matter of personal choice.

Whether there is a benefit of naming them identically is up for debate, but there is quite certainly no benefit to naming them differently. Most people would expect that importing module a would place symbols in namespace a rather than namespace A, and this is the case in other languages that modularise code (Java, C#, Python, etc.). Just because we can name modules and namespaces differently in C++ does not mean that we should.

and work toward stabilizing it so that it can eventually be included in the official repository.

I think the best way to achieve library stability for such a feature is to provide it in such a way that its contents are glaringly obvious, rather than behind a script. I do not know whether the script includes external dependencies or not, but even if it does not we still provide far less work for the user by providing the module as-is rather than having them run a script to do so.

@mikomikotaishi
Copy link
Author

@ocornut What are your thoughts on making library internals and backends accessible through modules, as opposed to just the main header?

@ocornut
Copy link
Owner

ocornut commented Nov 27, 2025

Sorry I accidentally posted an earlier answer before getting far enough in my thoughts, so discard the deleted post.

Again, are these parts of the library API?

Backends are public API. I guess they are some sort of optional sub-libraries,

A module is designed to encapsulate the public API of the library, if they want to change it they can always #include the internals header and create a new module to create whatever utilities or extensions they want. It's not the library's responsibility to create a public interface for internals, and that is extraneous work which if the library maintains at that point ought to just be an official API, and even including another header doesn't incur a compile-time penalty that would be any greater than compiling another module that includes that same header anyway.

That makes sense, but note that some users of imgui_internal.h would frequently include it.

Assuming modules are a required replacement for headers (due to either hard constraints, either perhaps C++ users zealotry/eagerness to use new stuff) then both internals and backends should be accessible the same way. If they are not a hard requirement then I'd assume it is easier to avoid it. It partly comes down to the question: what's the purpose of modules? Initially I guess they were aimed to improve compile-time (I've seen some reports that they eventually don't so much, the sorts of outcome I would expect from anything in modern C++ land).

I'd be interested in an actual breakdown/explanation as to why you care or need modules in the first place?
Arguably you can say that backends are typically included in less compilation units.

I also don't know how modules would affect the perception of API stability and access to IMGUI_DISABLE_OBSOLETE_FUNCTIONS blocks.

The only thing I am sure about at this point is that providing a "output" repo with just the module files is going to be more attractive to the end user than forcing them to know about a generator. But if we decide it is worth emitting a module for internals then maybe a generator makes sense.

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Nov 28, 2025

Well, the problems modules solve (to my knowledge) are:

  • Encapsulates a translation unit by having explicit control over what symbols are exposed.
  • Doesn't export macros (the new alternative is to handle conditional compilation through build system macros or inside the module itself).
  • Faster compilation time (they are essentially an upgrade from precompiled headers) and only have to be built once for each module, unlike headers which rebuild every time they are included. From what I was told import std; which imports the entire standard library is about ten times faster than #include <iostream> (which is just one header out of a hundred), but this was something presented by the committee and I have no stake to endorse anything they say so take it with a pinch of salt). I only think this might be a good call considering imgui.h has so many symbols that re-compiling them each time imgui.h is included would be cheaper for most.
  • There are probably more I'm forgetting right now, I can revisit this later.

I'm building some projects using purely modules (and very few headers unless necessary), which is part of why I am personally interested in module adoption. I do not know of any situation where modules are strictly necessary and headers would not be, besides similar passion projects to mine where I constrain myself to modules. Realistically, for the time being most people are still using C++17 or maybe even earlier, so there is not much demand for most consumers to use modules, I only proposed my PR because I think that for users of C++20+ the option to access the library through modules would be a great idiomatic improvement, though it isn't a hard requirement. (There are similar cases in other languages, for example Java 9 introducing JPMS yet some libraries haven't adopted it yet, but I digress.)

The IMGUI_DISABLE_OBSOLETE_FUNCTIONS blocks would probably have to be controlled either with -D flags. If we give direct, full control to the consumer over the library, we can allow them to #define macros right before #include "imgui.h" in the module. If I'm not mistaken, we already allow control over things through imconfig.h, and this config file will just get included into the module anyway.

In my personal opinion I think we should only modularise the imgui.h file, and then leave it up to consumers to choose whether or not to use the internals (since these are internal parts of the library). As for the backends, seeing as you say they should be exposed as public API, @stripe2933 I think we should make these partitions rather than separate modules entirely, but feel free to offer an alternative suggestion.

In the end, I don't know if you planned to allow anyone to access imgui_internals.h, and there is nothing stopping people from just continuing to use it through the header. But if it is part of the public API or you do want people to use it freely, perhaps we should just expose it in the module too.

Also, any thoughts on the module name? I think ImGui (which matches the namespace name) is the best choice here because it would make the most logical sense that if you wrote import ImGui; you would get ImGui::* symbols, just as import std; gives you std::*, import boost.*; gives you boost::*, import Poco; gives you Poco::*, etc. Most other libraries have this convention too as do most other languages (Java, C#, Python, etc.), and while it isn't a hard requirement in C++ it's the simplest for all.

@stripe2933
Copy link
Contributor

stripe2933 commented Nov 28, 2025

In my personal opinion I think we should only modularise the imgui.h file, and then leave it up to consumers to choose whether or not to use the internals (since these are internal parts of the library). As for the backends, seeing as you say they should be exposed as public API, @stripe2933 I think we should make these partitions rather than separate modules entirely, but feel free to offer an alternative suggestion.

This is good idea, but I gave up it because the relationship of imgui.h and imgui_XXX.h does not match to the relationship of primary module interface unit and module partition interface unit. I understood your suggestion as:

imgui.cppm

module;

#include <imgui.h>

export module imgui;

export { ... } // export symbols in imgui.h

imgui_internal.cppm

module;

#include <imgui_internal.h>

export module imgui:internal;

export { ... } // export symbols in imgui_internal.h

imgui_impl_glfw.cppm

module;

#include <imgui_impl_glfw.h>

export module imgui:glfw;

export { ... } // export symbols in imgui_impl_glfw.h

and end user just make the build system configuration file like:

CMakeLists.txt

add_library(imgui_module)
target_sources(imgui_module PUBLIC
    FILE_SET CXX_MODULES
    FILES imgui.cppm imgui_internal.cppm imgui_impl_glfw.cppm
)
target_link_libraries(imgui_module PRIVATE imgui::imgui)

and want to import all of them just with import imgui; statement, right? I also agree this is very clean and C++-ish solution. But for this, module partition interface units must be manually exported like:

diff --git a/imgui.cppm b/imgui.cppm
index 7fd68a0..25a7d6c 100644
--- a/imgui.cppm
+++ b/imgui.cppm
@@ -3,5 +3,7 @@ module;
 #include <imgui.h>
 
 export module imgui;
+export import :internal;
+export import :glfw;
 
export { ... } // export symbols in imgui.h

Therefore modifying the module export file is unavoidable. Unfortunately, modifying the third party library file is cumbersome when using package manager (e.g. vcpkg). So I didn't adopted this model.

In the end, I don't know if you planned to allow anyone to access imgui_internals.h, and there is nothing stopping people from just continuing to use it through the header. But if it is part of the public API or you do want people to use it freely, perhaps we should just expose it in the module too.

It is named as internal, but I perceive it as experimental. Anyway, the usage of imgui_internal module is optional.

Also, any thoughts on the module name? I think ImGui (which matches the namespace name) is the best choice here because it would make the most logical sense that if you wrote import ImGui; you would get ImGui::* symbols, just as import std; gives you std::, import boost.; gives you boost::, import Poco; gives you Poco::, etc. Most other libraries have this convention too as do most other languages (Java, C#, Python, etc.), and while it isn't a hard requirement in C++ it's the simplest for all.

Not all libraries follow this convention. Vulkan-Hpp uses vk:: namespace, but its module name was vulkan_hpp (and it will be changed to vulkan::), VulkanMemoryAllocator-Hpp uses vma:: namespace, but its module name is vk_mem_alloc_hpp.

In my honest opinion, choosing imgui versus ImGui is not really important subject (they're only differ at the case!). Also the module binding is already released, so I'm quite negative to introduce the breaking changes.

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Nov 28, 2025

Unfortunately, modifying the third party library file is cumbersome when using package manager (e.g. vcpkg). So I didn't adopted this model.

I don't think I understand. Why not use build system settings or extra conditional compilation flags to modify these settings? Seeing as I now know that these backends are indeed "public API" libraries, it's common for anyone to want access to potentially all of those headers. There isn't a compilation slow-down from importing a large module, anyway, so that isn't a concern. But having to manually import every single module rather than just a single import statement for the library seems very un-idiomatic here. I am sure we can find a better alternative.

I think it makes the most sense if we just provide one module. If we end up building all those other modules, then the module for imgui.h shouldn't be the main module, the main module should just be of the form

module;

export module ImGui;

export import ImGui.Main; // <imgui.h>
export import ImGui.Internals; // <imgui_internals.h>
export import ImGui.Glfw; // <backends/imgui_impl_*.h>, ...
export import ImGui.Sdl;
export import ImGui.Vulkan;
// etc.

For this, we don't even have to use partitions. Each module can remain a full module, that is just exported by the final ImGui module. The only downside to this, is that it can clutter language server import suggestions with unwanted autocomplete features if most would prefer to just use the final full module.

Not all libraries follow this convention. Vulkan-Hpp uses vk:: namespace, but its module name was vulkan_hpp (and it will be changed to vulkan::), VulkanMemoryAllocator-Hpp uses vma:: namespace, but its module name is vk_mem_alloc_hpp.

Not everything follows this convention (and to be honest I don't know why they added the _hpp to the end of the module) but choosing something simpler is best for everyone. There is not widespread adoption of modules yet, but in the long run I think there is greater benefit by forming conventions that are simple. (After all, I am basing this off of existing long-term conventions such as Java/C# where modules and namespaces are directly tied, the only reason I can think of that this wasn't forced in C++ is because namespaces are not forced and modules cannot break backwards compatibility.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants