Why is this minimal button widget not connecting up correctly?

Hi,

I have just written the above minimal project: a window with a single button. Pressing the button should change the button’s text.

CMakeLists.txt

cmake_minimum_required(VERSION 3.9)
project (TestRoot CXX)
find_package(ROOT REQUIRED COMPONENTS RIO Core Gpad Gui)
set(SOURCES main.cpp MainFrame.hpp MainFrame.cpp)
add_executable(TestRoot ${SOURCES})
target_link_libraries(TestRoot ROOT::RIO ROOT::Core ROOT::Gpad ROOT::Gui)
target_include_directories(TestRoot PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
ROOT_GENERATE_DICTIONARY(MainFrame_dict MainFrame.hpp MODULE TestRoot LINKDEF LinkDef.h)

LinkDef.h

#ifdef __CLING__
#pragma link C++ class MainFrame;
#endif // __CLING__

main.cpp

#include <TApplication.h>
#include "MainFrame.hpp"
#include <iostream>
int main(int argc, char* argv[]) {
	TApplication theApp("App", &argc, argv);
	MainFrame frame(gClient->GetRoot(), 600, 600);
	theApp.Run(true);
	return 0;
}

MainFrame.cpp

#include "MainFrame.hpp"
#include <TApplication.h>
#include "TGFrame.h"
#include <TGButton.h>

void MainFrame::Press() {
    button->SetText("Pressed");
}

MainFrame::MainFrame(const TGWindow* p, unsigned w, unsigned h) : TGMainFrame(p, w, h) {
    button = new TGTextButton(this, "Unpressed");
    button->Connect("Clicked()", "MainFrame", this, "Press()");
    this->AddFrame(button, nullptr);
    MapSubwindows();
    Resize(GetDefaultSize());
    MapWindow();
}

MainFrame.hpp

#include <TGFrame.h>
class MainFrame : public TGMainFrame {
public:
    MainFrame(const TGWindow* p, unsigned w, unsigned h);
    void Press();
    TGTextButton* button;
    ClassDef(MainFrame, 0);
};

The program compiles and runs but gives the following runtime error:

IncrementalExecutor::executeFunction: symbol '_ZN9MainFrame5PressEv' unresolved while linking symbol '__cf_2'!
You are probably missing the definition of MainFrame::Press()
Maybe you need to load the corresponding shared library?
Error in <TClingCallFunc::make_wrapper>: Failed to compile
  ==== SOURCE BEGIN ====
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"
__attribute__((used)) __attribute__((annotate("__cling__ptrcheck(off)")))
extern "C" void __cf_2(void* obj, int nargs, void** args, void* ret)
{
   ((MainFrame*)obj)->Press();
   return;
}
#pragma clang diagnostic pop
  ==== SOURCE END ====
Error in <TClingCallFunc::Exec(address, interpVal)>: Called with no wrapper, not implemented!
IncrementalExecutor::executeFunction: symbol '_ZN9MainFrame5PressEv' unresolved while linking symbol '__cf_3'!
You are probably missing the definition of MainFrame::Press()
Maybe you need to load the corresponding shared library?
Error in <TClingCallFunc::make_wrapper>: Failed to compile
  ==== SOURCE BEGIN ====
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"
__attribute__((used)) __attribute__((annotate("__cling__ptrcheck(off)")))
extern "C" void __cf_3(void* obj, int nargs, void** args, void* ret)
{
   ((MainFrame*)obj)->Press();
   return;
}
#pragma clang diagnostic pop
  ==== SOURCE END ====
Error in <TClingCallFunc::Exec(address, interpVal)>: Called with no wrapper, not implemented!
free(): invalid pointer
Aborted (core dumped)

Why is the button not connecting up correctly? What do I need to change here?

If it helps I installed ROOT with

sudo apt install build-essential
sudo apt-get install git
wget https://root.cern/download/root_v6.28.04.Linux-ubuntu22-x86_64-gcc11.3.tar.gz
tar -xzvf root_v6.28.04.Linux-ubuntu22-x86_64-gcc11.3.tar.gz
source root/bin/thisroot.sh

ROOT Version: 6.28.04
Platform: Ubuntu 22
Compiler: g++


Good question, apparently the signal/slot mechanism is broken… We’ll investigate.

Hi Bertie,

I have tried all the ROOT minor versions that support Ubuntu 22. Attempting to run the Ubuntu 20 distribution on an Ubuntu 22 machine gives me linktime errors complaining about OpenSSL. Installing OpenSSL made no difference to the linker error messages.

I am about to uninstall Ubuntu 22 on my machine and install Ubuntu 20, and work my way through a series of operating systems however this approach feels a little heavy-handed. Could you suggest an operating system, ROOT version combination where you have successfully used the SIGNAL/SLOT mechanism?

As I said, we need to investigate in order to know since when it is broken. And FYI it has nothing to do with the OS version, I see the same behavior on Windows…

So here is more information. Using a library for the signal/slots work. E.g. can you try with this CMakeLists.txt and let me know?

cmake_minimum_required(VERSION 3.9)

project (TestRoot CXX)

find_package(ROOT REQUIRED COMPONENTS RIO Core Gpad Gui)
include(${ROOT_USE_FILE})
set(CMAKE_CXX_FLAGS "${ROOT_CXX_FLAGS}")
include_directories(${ROOT_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})

ROOT_GENERATE_DICTIONARY(MainFrame_dict MainFrame.hpp MODULE MainFrame LINKDEF LinkDef.h)
add_library(libMainFrame SHARED MainFrame.cpp MainFrame_dict.cxx)
target_link_libraries(libMainFrame ROOT::RIO ROOT::Core ROOT::Gpad ROOT::Gui)
if(MSVC)
  set_target_properties(libMainFrame PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif()

add_executable(TestRoot main.cpp)
target_link_libraries(TestRoot libMainFrame)

And stay tuned, we might have a correct solution (trying right now)

Here is the proper solution. Just add this line in your CMakeLists.txt:

set_target_properties(TestRoot PROPERTIES ENABLE_EXPORTS 1)

To expose the symbols to the interpreter. And sorry for the long delay…

This works for my above minimal example but not my actual project. I’ll try and produce a new minimal example now!

Edit:
Actually this appears to work generally. Using an IDE like VSCode, I need to manually call cmake . && cmake --build . rather than press the play button, but that’s a small issue.

1 Like

Right. At the end, your CMakeLists.txt should look like something like this:

cmake_minimum_required(VERSION 3.9)

project (TestRoot CXX)

find_package(ROOT REQUIRED COMPONENTS RIO Core Gpad Gui)
include(${ROOT_USE_FILE})
set(CMAKE_CXX_FLAGS "${ROOT_CXX_FLAGS}")
include_directories(${ROOT_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})

set(SOURCES main.cpp MainFrame.hpp MainFrame.cpp)

ROOT_GENERATE_DICTIONARY(MainFrame_dict MainFrame.hpp LINKDEF LinkDef.h)
add_executable(TestRoot ${SOURCES} MainFrame_dict.cxx)
set_target_properties(TestRoot PROPERTIES ENABLE_EXPORTS 1)
target_link_libraries(TestRoot ROOT::RIO ROOT::Core ROOT::Gpad ROOT::Gui)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.