dearimgui / dear_bindings

C header (and language binding metadata) generator for Dear ImGui
MIT License
221 stars 12 forks source link

Mark synthetic functions as such #20

Closed rokups closed 2 years ago

rokups commented 2 years ago

I came to a need of this when i tried to consume API json file directly and write a generator that outputs pybind11 code to produce python bindings. In such case it is required to know which functions actually exist in the library and should be wrapped and which ones are generated. Due to how pybind11 works there is no need to wrap a C api and i can feed C++ api to it directly. I am not sure if this is complete/correct so please review.

ocornut commented 2 years ago

Would probably need a bit of extra commentary + updating docs/MetadataFormat Can you also provide some concrete example of where this is going to be useful?

rokups commented 2 years ago

pybind11 is pure-c++ library that implements bindings to python. We do roughly this:

#include <pybind11/pybind11.h>
namespace cimgui { extern "C" {
#include "c_generated/cimgui.h"
#include "c_generated/cimgui_internal.h"
} }
#include <imgui.h>
#include <imgui_internal.h>
#include "typecasters.hpp"

namespace py = pybind11;

void bind_funcs(pybind11::module_& m)
{
    m.def("CreateContextEx", &ImGui::CreateContext, py::return_value_policy::reference);
    m.def("DestroyContextEx", &ImGui::DestroyContext, py::return_value_policy::automatic);
    m.def("GetCurrentContext", &ImGui::GetCurrentContext, py::return_value_policy::reference);
    m.def("SetCurrentContext", &ImGui::SetCurrentContext, py::return_value_policy::automatic);
    m.def("GetIO", &ImGui::GetIO, py::return_value_policy::reference);
    m.def("GetStyle", &ImGui::GetStyle, py::return_value_policy::reference);
    m.def("NewFrame", &ImGui::NewFrame, py::return_value_policy::automatic);
    m.def("EndFrame", &ImGui::EndFrame, py::return_value_policy::automatic);
    m.def("Render", &ImGui::Render, py::return_value_policy::automatic);
    // And many more...
}

It feeds existing C++ functions to pyblind11 for bindings to be generated. Since JSON also contains generated functions that arent available outside of cimgui i needed to know which functions correspond to the original and which ones are newly injected so i can avoid binding injected functions (python can support default arguments therefore there is no need to wrap these helper functions).

ShironekoBen commented 2 years ago

Hm... I'm confused as to what counts as "synthetic" in this case - I think I must be missing something because I keep thinking "if you can feed C++ functions to pybind11 then why is Dear Bindings needed at all?". Is what you're looking for metadata for the original C++ API rather than the newly-generated C-one, maybe?

rokups commented 2 years ago
CIMGUI_API bool ImGui_DragIntEx(const char* label, int* v, float v_speed /* = 1.0f */, int v_min /* = 0 */, int v_max /* = 0 */, const char* format /* = "%d" */, ImGuiSliderFlags flags /* = 0 */); // If v_min >= v_max we have no bound
CIMGUI_API bool ImGui_DragInt(const char* label, int* v);                                                     // Implied v_speed = 1.0f, v_min = 0, v_max = 0, format = "%d", flags = 0

This would be an example of synthetic vs non-synthetic. ImGui_DragIntEx is non-synthetic because it corresponds to original ImGui::DragInt. ImGui_DragInt on the other hand has no exact counterpart on C++ side and was generated - it is synthetic.

rokups commented 2 years ago

if you can feed C++ functions to pybind11 then why is Dear Bindings needed at all?

Forgot that one. Dear Bindings produces a very useful JSON with all the API, including argument types and what not. Using this JSON is very convenient for generating bindings with pybind11.

ShironekoBen commented 2 years ago

Ah, gotcha - I understand now!

So in the case of ImGui_DragInt() at least, you can tell them apart by looking at is_default_argument_helper, which is true in the case of functions that have been generated as ways to substitute for default arguments. Similarly is_manual_helper is true on manually-added helper functions (just ImVector_Construct and ImVector_Destruct at present).

Off the top of my head I'm drawing a blank on other functions that get added programmatically to the API, so I think that just ORing those two together may get you the result you want, but I may well be forgetting something... do you know of another case I've missed that needs tagging?

rokups commented 2 years ago

I did not realize i could use is_default_argument_helper+is_manual_helper. Sounds like they cover this usecase already.

do you know of another case I've missed that needs tagging?

Not at the moment. I did not do that much work on python bindings. If i stumble upon something i will surely let you know 👍🏻

Seems like we can close this PR then, thanks! 🙏🏻