ESP-IDF v5.0 and v5.-master - undefined reference to base classes

dmitrij999
Posts: 64
Joined: Sat Mar 02, 2019 8:06 pm

ESP-IDF v5.0 and v5.-master - undefined reference to base classes

Postby dmitrij999 » Thu Feb 09, 2023 12:19 pm

Hello everyone!

I'm designing the multilingual menu for SSD1306 and I chose the using abstract classes approach for this.
The generic screen class is an abstract class ScreenBuilder, which other screen types inherite from, such as:
- MenuBuilder
- HomescreenBuilder
- SettingScreenBuilder
- NotificationScreenBuilder

To implement multilinguality, I created a MenuItemsOnLanguage as an abstract class for labels exist in the project, and 2 classes which inherits from this and implements returning labels on the language:
- MenuItemsOnEnglish
- MenuItemsOnRussian

After all, I declare the reference with type of abstract class to be able to substitute the screen and language.
Like this:

Code: Select all

MenuItemsOnLanguage * screen_labels;
// MenuItemsOnEnglish * english;
// MenuItemsOnRussian * russian;


ScreenBuilder * current_screen = nullptr;

HomescreenBuilder * home_screen = nullptr;
MenuBuilder * main_menu_screen = nullptr;

SettingScreenBuilder * brightness = nullptr;
SettingScreenBuilder * volume = nullptr;
...
MenuBuilder * wifi_connection_timeout = nullptr;
MenuBuilder * language_select = nullptr;
SettingScreenBuilder * reset_to_defaults_screen = nullptr;
SettingScreenBuilder * about_software_screen = nullptr;

NotificationScreenBuilder * notif_to_show = nullptr;
With using this approach, IDF v4.4 compiles it, but IDF v5.0 gives errors:

Code: Select all

/home/espidf5/.espressif/tools/xtensa-esp32s3-elf/esp-2022r1-11.2.0/xtensa-esp32s3-elf/bin/../lib/gcc/xtensa-esp32s3-elf/11.2.0/../../../../xtensa-esp32s3-elf/bin/ld: esp-idf/interface_manager/libinterface_manager.a(interface_organize.cpp.obj):(.literal._Z23interface_organize_initP11u8g2_struct+0x0): undefined reference to `_ZTV19MenuItemsOnLanguage'
/home/espidf5/.espressif/tools/xtensa-esp32s3-elf/esp-2022r1-11.2.0/xtensa-esp32s3-elf/bin/../lib/gcc/xtensa-esp32s3-elf/11.2.0/../../../../xtensa-esp32s3-elf/bin/ld: esp-idf/interface_manager/libinterface_manager.a(setting_screen_builder.cpp.obj):(.literal._ZN20SettingScreenBuilderC2EP11u8g2_structNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE+0x0): undefined reference to `_ZTV13ScreenBuilder'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
ninja failed with exit code 1, output of the command is in the /home/espidf5/esp32-workspace/esp32s3-voice/build/log/idf_py_stderr_output_8751 and /home/espidf5/esp32-workspace/esp32s3-voice/build/log/idf_py_stdout_output_8751
Any ideas?

ESP_jakob
Posts: 47
Joined: Mon Jun 01, 2020 6:28 am

Re: ESP-IDF v5.0 and v5.-master - undefined reference to base classes

Postby ESP_jakob » Fri Feb 10, 2023 7:56 am

_ZTV* symbols are vtables. You could check the following for the related classes:

Make sure that all object files are provided (i.e., all *.cpp files are added to the SRCS parameter in idf_component_register() )

Another issue could be that some definitions of pure virtual functions are missing.

dmitrij999
Posts: 64
Joined: Sat Mar 02, 2019 8:06 pm

Re: ESP-IDF v5.0 and v5.-master - undefined reference to base classes

Postby dmitrij999 » Fri Feb 10, 2023 10:28 am

Here is the CmakeLists contents for the component

Code: Select all

set(component_srcs "interface_manager.cpp" "u8g2_esp32_hal.cpp" "menu_builder.cpp" "screen_builder.cpp" "interface_organize.cpp" "notification_screen_builder.cpp" "setting_screen_builder.cpp" "homescreen_builder.cpp" )
idf_component_register(SRCS "${component_srcs}"
                       INCLUDE_DIRS "include"
                       PRIV_INCLUDE_DIRS ""
                       PRIV_REQUIRES "driver" "analog_inputs_manager" "system_manager" "ip5328p_driver"
                       REQUIRES "u8g2")
ScreenBuilder is an abstract class with some implementations. Pure virtual methods and class declaration are in header files

screen_builder.hpp

Code: Select all

#ifndef SCREEN_BUILDER_H
#define SCREEN_BUILDER_H
#include <u8g2.h>
#include <string>
using namespace std;
class ScreenBuilder {
    public:
        void set_left_key(string left_key_text, void (*on_press_cb)());
        void set_right_key(string right_key_text, void (*on_press_cb)());
        void set_left(void (*on_press_cb)());
        void set_right(void (*on_press_cb)());
        void set_up(void (*on_press_cb)());
        void set_down(void (*on_press_cb)());
        // void run_left_key_action();
        // void run_right_key_action();
        void draw_frame();
        void draw_title();
        virtual void draw_contents();
        virtual void render();
        void send_buffer();
        void (*on_left_key_click_cb)() = nullptr;
        void (*on_right_key_click_cb)() = nullptr;
        void (*on_left_click_cb)() = nullptr;
        void (*on_right_click_cb)() = nullptr;
        void (*on_up_click_cb)() = nullptr;
        void (*on_down_click_cb)() = nullptr;
    protected:
        u8g2_t * _u8g2;
        string _title;
        string _left_key_text;
        string _right_key_text;
};
#endif
As for lang classes, all the lang classes are in header files, and lang class for the certain language has inline implementation in header file

lang_en.hpp

Code: Select all

#ifndef LANG_EN_H
#define LANG_EN_H
#include "langs_class.hpp"
class MenuItemsOnEnglish : public MenuItemsOnLanguage {
    public:
        MenuItemsOnEnglish() {}
        string main_menu(){ return "Main menu"; }
        string cancel(){ return "Cancel"; }
        string turn_off_question(){ return "Turn off?"; }
        string reset_to_defaults(){ return "Reset to defaults"; }
        string brightness(){ return "Brightness"; }
        string usb_close_inactive_timeout(){ return "Inactive USB timeout"; }
        string wifi_close_inactive_timeout(){ return "Inactive WiFi timeout"; }
        ...
};
#endif
lang_ru.hpp

Code: Select all

#ifndef LANG_RU_H
#define LANG_RU_H
#include "langs_class.hpp"
class MenuItemsOnRussian : public MenuItemsOnLanguage {
    public:
        MenuItemsOnRussian() {}
        string main_menu(){ return "Главное меню"; }
        string cancel(){ return "Отмена"; }
        string turn_off_question(){ return "Выключить?"; }
        string reset_to_defaults(){ return "Сбросить настройки"; }
        string brightness(){ return "Яркость"; }
        string usb_close_inactive_timeout(){ return "Таймаут неакт.USB"; }
        string wifi_close_inactive_timeout(){ return "Таймаут неакт.WiFi"; }
       ...
};
#endif
langs_class.hpp

Code: Select all

#ifndef LANGS_CLASS_H
#define LANGS_CLASS_H
#include <string>
using namespace std;
// typedef struct {
//     char item_name[128];
//     void (*on_item_select_cb)();
// } menu_item_t;
    
class MenuItemsOnLanguage {
    public:
        virtual string main_menu();
        virtual string cancel();
        virtual string turn_off_question();
        virtual string reset_to_defaults();
        virtual string brightness();
        virtual string usb_close_inactive_timeout();
        virtual string wifi_close_inactive_timeout();
...
};
#endif
The thing is that, IDF v4.4 compiles this code, IDF v5 not

ESP_jakob
Posts: 47
Joined: Mon Jun 01, 2020 6:28 am

Re: ESP-IDF v5.0 and v5.-master - undefined reference to base classes

Postby ESP_jakob » Fri Feb 10, 2023 11:35 am

What happens if you make all virtual functions in `MenuItemsOnLanguage` pure virtual? I.e.,

Code: Select all

virtual string main_menu() = 0
, etc.

Do you still see the following error then?

Code: Select all

undefined reference to `_ZTV19MenuItemsOnLanguage'

dmitrij999
Posts: 64
Joined: Sat Mar 02, 2019 8:06 pm

Re: ESP-IDF v5.0 and v5.-master - undefined reference to base classes

Postby dmitrij999 » Fri Feb 10, 2023 6:39 pm

ESP_jakob wrote:
Fri Feb 10, 2023 11:35 am
What happens if you make all virtual functions in `MenuItemsOnLanguage` pure virtual? I.e.,

Code: Select all

virtual string main_menu() = 0
, etc.
Yes, it helped me
Both stable IDF v5 and IDF v5.-master compiled my code
Thanks ;)

ESP_jakob
Posts: 47
Joined: Mon Jun 01, 2020 6:28 am

Re: ESP-IDF v5.0 and v5.-master - undefined reference to base classes

Postby ESP_jakob » Mon Feb 13, 2023 1:45 am

This is probably because the functions in your abstract classes were not pure virtual, i.e., with the =0 in the end. Hence, the linker will still look for a vtable for that class. But because the function is never implemented for the base class, a vtable is never generated for it. Hence the error message. In fact, the base class was not abstract as there was no pure virtual function.

Btw., we are using at least C++11 in IDF. In C++11 and above you can use the override indentifier to mark functions that must override from a base class. I'd suggest using it since the error messages from the compiler will be much clearer in case of a mismatch of original and overridden function.

Who is online

Users browsing this forum: Google [Bot] and 125 guests