dearimgui / dear_bindings

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

Linker can't find functions from cimgui.cpp #36

Closed Durobot closed 11 months ago

Durobot commented 11 months ago

I'm sorry for opening an issue, it's most probably something I'm missing, but I could not find a discussion forum of any kind for dear_bindings.

While trying to build example_null.c, I'm getting linker errors like these:

/usr/bin/ld: ./build/main.o: in function `main':
/home/ ... /dear_bindings_01/main.c:14:(.text+0x24): undefined reference to `ImGui_CreateContext'
/usr/bin/ld: /home/ ... /dear_bindings_01/main.c:15:(.text+0x29): undefined reference to `ImGui_GetIO'
/usr/bin/ld: /home/ ... /dear_bindings_01/main.c:20:(.text+0x57): undefined reference to `ImFontAtlas_GetTexDataAsRGBA32'
/usr/bin/ld: /home/ ... /dear_bindings_01/main.c:28:(.text+0xb4): undefined reference to `ImGui_NewFrame'
. . .

I'm certainly not the greatest expert on C++ (to put it mildly), but it does look like the linker has a point here - these functions are declared in cimgui.h like this:

CIMGUI_API ImGuiContext* ImGui_CreateContext(ImFontAtlas* shared_font_atlas /* = NULL */);
CIMGUI_API ImGuiIO*    ImGui_GetIO(void);
CIMGUI_API void                   ImFontAtlas_GetTexDataAsRGBA32(ImFontAtlas* self, unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel /* = NULL */);
CIMGUI_API void        ImGui_NewFrame(void);

But their bodies in cimgui.cpp are located in a namespace, called cimgui:

// Wrap this in a namespace to keep it separate from the C++ API
namespace cimgui
{
#include "cimgui.h"
}

CIMGUI_API cimgui::ImGuiContext* cimgui::ImGui_CreateContext(cimgui::ImFontAtlas* shared_font_atlas)
CIMGUI_API cimgui::ImGuiIO* cimgui::ImGui_GetIO(void)
CIMGUI_API void                   cimgui::ImFontAtlas_GetTexDataAsRGBA32(cimgui::ImFontAtlas* self, unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
CIMGUI_API void        cimgui::ImGui_NewFrame(void)

AFAIK, C does not support namespaces (at least, in the same sense as C++ does), so how do I call these functions from C code?

ShironekoBen commented 11 months ago

Hm, that's interesting. You're right in that C doesn't support namespaces, but in this case that is "helpful", in that specifying C linkage should cause the compiler/linker to ignore namespaces. Specifically the C++ standard 7.5.6 states that:

Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function.

...and the example given shows that A::f(), B::f() and f() (where A and B are namespaces) all refer to the same function when C linkage is specified.

That's the basis on which the C header/implementation file here works - it doesn't matter that the header defines ImGui_GetIO() whilst the CPP file sticks it in a namespace and calls it cimgui::ImGui_GetIO(), because the extern C specification should make the linker treat them as the same function.

Which is all a rather tortuous way of saying "that sounds like either a compiler bug or some sort of command-line flags/#define/similar issue". What compiler and settings are you using to compile in this case?

(oh, and don't worry about opening issues! There isn't an official forum or anything, and this sort of thing could easily be either a bug in Dear Bindings or something that whilst "technically" not a bug would be useful to avoid if people are going to run into it on some specific compiler setup, so I'm very happy to see it reported!)

Durobot commented 11 months ago

Having gathered all the info for the post below, I have discovered I actually somehow managed to miss cimgui.o in the linker (gcc) command. Shame on me for raising a hubbub. It does compile and link, after all. The namespace was a red herring.

My original post follows.


Of course, I should have provided my build environment and script. I'm on Arch Linux: Linux archvm 6.4.7-arch1-2 #1 SMP PREEMPT_DYNAMIC Mon, 31 Jul 2023 11:41:04 +0000 x86_64 GNU/Linux Compiler is gcc (GCC) 13.2.1 20230801.

I'm trying to build the provided example_null.c, renamed to main.c. Here's my quick and dirty build script:

#!/bin/sh
BUILD_PATH='./build'
DEAR_BINDINGS_PATH='../../dear_bindings'
IMGUI_PATH="../../imgui"
IMGUI_BACKEND_PATH="${IMGUI_PATH}/backends"

echo "g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui.o ${IMGUI_PATH}/imgui.cpp"
g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui.o ${IMGUI_PATH}/imgui.cpp

echo "g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_demo.o ${IMGUI_PATH}/imgui_demo.cpp"
g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_demo.o ${IMGUI_PATH}/imgui_demo.cpp

echo "g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_draw.o ${IMGUI_PATH}/imgui_draw.cpp"
g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_draw.o ${IMGUI_PATH}/imgui_draw.cpp

echo "g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_tables.o ${IMGUI_PATH}/imgui_tables.cpp"
g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_tables.o ${IMGUI_PATH}/imgui_tables.cpp

echo "g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_widgets.o ${IMGUI_PATH}/imgui_widgets.cpp"
g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_widgets.o ${IMGUI_PATH}/imgui_widgets.cpp

echo "g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_impl_glfw.o ${IMGUI_BACKEND_PATH}/imgui_impl_glfw.cpp"
g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_impl_glfw.o ${IMGUI_BACKEND_PATH}/imgui_impl_glfw.cpp

echo "g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_impl_opengl3.o ${IMGUI_BACKEND_PATH}/imgui_impl_opengl3.cpp"
g++ -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/imgui_impl_opengl3.o ${IMGUI_BACKEND_PATH}/imgui_impl_opengl3.cpp

echo "g++ -std=c++11 -DIMGUI_IMPL_API=\"extern \\\"C\\\" \" -I${DEAR_BINDINGS_PATH} -I${IMGUI_PATH} -g -Wall -Wformat -c -o ${BUILD_PATH}/cimgui.o ${DEAR_BINDINGS_PATH}/cimgui.cpp"
g++ -std=c++11 -DIMGUI_IMPL_API="extern \"C\" " -I${DEAR_BINDINGS_PATH} -I${IMGUI_PATH} -g -Wall -Wformat -c -o ${BUILD_PATH}/cimgui.o ${DEAR_BINDINGS_PATH}/cimgui.cpp

echo "gcc -I${DEAR_BINDINGS_PATH} -I${IMGUI_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/main.o main.c"
gcc -I${DEAR_BINDINGS_PATH} -I${IMGUI_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -c -o ${BUILD_PATH}/main.o main.c

#g++ -o example_glfw_opengl3 main.o imgui.o imgui_demo.o imgui_draw.o imgui_tables.o imgui_widgets.o imgui_impl_glfw.o imgui_impl_opengl3.o -std=c++11 -I${IMGUI_PATH} -I${IMGUI_BACKEND_PATH} -g -Wall -Wformat `pkg-config --cflags glfw3` -lGL `pkg-config --static --libs glfw3`

# -- No difference with gcc below --
#echo "g++ -o ${BUILD_PATH}/dear_bindings_01 ${BUILD_PATH}/main.o ${BUILD_PATH}/imgui.o ${BUILD_PATH}/imgui_demo.o ${BUILD_PATH}/imgui_draw.o ${BUILD_PATH}/imgui_tables.o ${BUILD_PATH}/imgui_widgets.o ${BUILD_PATH}/imgui_impl_glfw.o ${BUILD_PATH}/imgui_impl_opengl3.o -g -Wall -Wformat `pkg-config --cflags glfw3` -lGL `pkg-config --static --libs glfw3`"
#g++ -o ${BUILD_PATH}/dear_bindings_01 ${BUILD_PATH}/main.o ${BUILD_PATH}/imgui.o ${BUILD_PATH}/imgui_demo.o ${BUILD_PATH}/imgui_draw.o ${BUILD_PATH}/imgui_tables.o ${BUILD_PATH}/imgui_widgets.o ${BUILD_PATH}/imgui_impl_glfw.o ${BUILD_PATH}/imgui_impl_opengl3.o -g -Wall -Wformat `pkg-config --cflags glfw3` -lGL `pkg-config --static --libs glfw3`

echo "gcc -o ${BUILD_PATH}/dear_bindings_01 ${BUILD_PATH}/main.o ${BUILD_PATH}/imgui.o ${BUILD_PATH}/imgui_demo.o ${BUILD_PATH}/imgui_draw.o ${BUILD_PATH}/imgui_tables.o ${BUILD_PATH}/imgui_widgets.o ${BUILD_PATH}/imgui_impl_glfw.o ${BUILD_PATH}/imgui_impl_opengl3.o -g -Wall -Wformat `pkg-config --cflags glfw3` -lGL -lstdc++ `pkg-config --static --libs glfw3`"
gcc -o ${BUILD_PATH}/dear_bindings_01 ${BUILD_PATH}/main.o ${BUILD_PATH}/imgui.o ${BUILD_PATH}/imgui_demo.o ${BUILD_PATH}/imgui_draw.o ${BUILD_PATH}/imgui_tables.o ${BUILD_PATH}/imgui_widgets.o ${BUILD_PATH}/imgui_impl_glfw.o ${BUILD_PATH}/imgui_impl_opengl3.o -g -Wall -Wformat `pkg-config --cflags glfw3` -lGL -lstdc++ `pkg-config --static --libs glfw3`

cimgui.h and cimgui.cpp are the wrapper generated with dear_bindings from dear imgui v1.89.8:

python dear_bindings.py -o cimgui ../imgui/imgui.h

When I run my build script, it compiles everything, but fails at the link step:

 ./build.sh 
g++ -std=c++11 -I../../imgui -I../../imgui/backends -g -Wall -Wformat  -c -o ./build/imgui.o ../../imgui/imgui.cpp
g++ -std=c++11 -I../../imgui -I../../imgui/backends -g -Wall -Wformat  -c -o ./build/imgui_demo.o ../../imgui/imgui_demo.cpp
g++ -std=c++11 -I../../imgui -I../../imgui/backends -g -Wall -Wformat  -c -o ./build/imgui_draw.o ../../imgui/imgui_draw.cpp
g++ -std=c++11 -I../../imgui -I../../imgui/backends -g -Wall -Wformat  -c -o ./build/imgui_tables.o ../../imgui/imgui_tables.cpp
g++ -std=c++11 -I../../imgui -I../../imgui/backends -g -Wall -Wformat  -c -o ./build/imgui_widgets.o ../../imgui/imgui_widgets.cpp
g++ -std=c++11 -I../../imgui -I../../imgui/backends -g -Wall -Wformat  -c -o ./build/imgui_impl_glfw.o ../../imgui/backends/imgui_impl_glfw.cpp
g++ -std=c++11 -I../../imgui -I../../imgui/backends -g -Wall -Wformat  -c -o ./build/imgui_impl_opengl3.o ../../imgui/backends/imgui_impl_opengl3.cpp
g++ -std=c++11 -DIMGUI_IMPL_API="extern \"C\" " -I../../dear_bindings -I../../imgui -g -Wall -Wformat -c -o ./build/cimgui.o ../../dear_bindings/cimgui.cpp
gcc -I../../dear_bindings -I../../imgui -g -Wall -Wformat  -c -o ./build/main.o main.c
gcc -o ./build/dear_bindings_01 ./build/main.o ./build/imgui.o ./build/imgui_demo.o ./build/imgui_draw.o ./build/imgui_tables.o ./build/imgui_widgets.o ./build/imgui_impl_glfw.o ./build/imgui_impl_opengl3.o -g -Wall -Wformat  -lGL -lstdc++ -lglfw -lrt -lm -ldl -lX11 -lpthread -lxcb -lXau -lXdmcp 
/usr/bin/ld: ./build/main.o: in function `main':
/home/archie/projects/c-playground/dear_bindings_01/main.c:14:(.text+0x24): undefined reference to `ImGui_CreateContext'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:15:(.text+0x29): undefined reference to `ImGui_GetIO'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:20:(.text+0x57): undefined reference to `ImFontAtlas_GetTexDataAsRGBA32'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:28:(.text+0xb4): undefined reference to `ImGui_NewFrame'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:31:(.text+0xc8): undefined reference to `ImGui_Text'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:32:(.text+0xf3): undefined reference to `ImGui_SliderFloat'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:33:(.text+0x149): undefined reference to `ImGui_Text'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:34:(.text+0x153): undefined reference to `ImGui_ShowDemoWindow'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:36:(.text+0x158): undefined reference to `ImGui_Render'
/usr/bin/ld: /home/archie/projects/c-playground/dear_bindings_01/main.c:40:(.text+0x17f): undefined reference to `ImGui_DestroyContext'
collect2: error: ld returned 1 exit status

(as you can see, I have tried defining IMGUI_IMPL_API as "extern \"C\" " when compiling cimgui.cpp, but that did nothing)

ShironekoBen commented 11 months ago

Great, glad you were able to resolve it. We all miss the simple things sometimes!