This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

undefined reference to `vtable for __cxxabiv1::__si_class_type_info'

Hey - I know there's no official support for C++ on this platform, but are there any other intrepid devs out there making this work?

I was able to build, flash and run my app, but virtual functions were not working (virtual calls in the base class calling the base implementation rather than the derived implementation).

I tried turning on rtti in the compile, but that is producing the captioned undefined reference. Searching I've done suggests the linker needs to be directed to link to C++ runtime ( -lstdc++ ) but this option does not appear to be supported by the SEGGER linker. Linker version info is below.

C:\Program Files\SEGGER\SEGGER Embedded Studio for ARM 4.12\gcc\arm-none-eabi\bin>ld --version
GNU ld (GNU Binutils) 2.30.0.20180329

Anyone out there got virtual functions working on this platform (nrf52832, Segger embedded studio)?

  • OK. I've got this sorted.  Turns out the two issues are unrelated: 

    Turning off RTTI support (which I don't need) gets rid of the undefined reference.

    The vtable problem arose from the fact that I was trying to call the derived class method from within the base class constructor - i.e. before the derived class was fully constructed. Crap. I used to know that :-$

    Refactoring this flow out of the base ctor and calling it explicitly after the derived class was constructed resolved the issue.

    Apart from that, in case it helps anybody else, here are the other issues I faced and how they were resolved:

    1. C "_Static_assert" vs. C++ "static_assert"

    In app_util.h:

    #ifdef __cplusplus
    #define STATIC_ASSERT_SIMPLE(EXPR)      static_assert(EXPR, "unspecified message")
    #define STATIC_ASSERT_MSG(EXPR, MSG)    static_assert(EXPR, MSG)
    #else
    #define STATIC_ASSERT_SIMPLE(EXPR)      _Static_assert(EXPR, "unspecified message")
    #define STATIC_ASSERT_MSG(EXPR, MSG)    _Static_assert(EXPR, MSG)
    #endif
    

    2. Beyond that, there are many cases of C++ not liking this style of C struct initialization:

    #define GPIOTE_CONFIG_OUT_SIMPLE(init_high)                                                        \
        {                                                                                              \
            .init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW,    \
            .task_pin = false,                                                                         \
        }
    #endif

    I solved this by adding a constructor to the struct: 

    #ifdef __cplusplus
    typedef struct nrf_drv_gpiote_out_config_t_struct
    {
        nrf_drv_gpiote_out_config_t_struct( nrf_gpiote_polarity_t polarity, nrf_gpiote_outinit_t state, bool isTaskPin )
            : action( polarity ), init_state( state ), task_pin( isTaskPin) {}
    #else        
    typedef struct 
    {
    #endif
        nrf_gpiote_polarity_t action;    /**< Configuration of the pin task. */
        nrf_gpiote_outinit_t  init_state; /**< Initial state of the output pin. */
        bool                  task_pin;  /**< True if the pin is controlled by a GPIOTE task. */
    } nrf_drv_gpiote_out_config_t;

    and changing the define to a constructor call:

    /**@brief Macro for configuring a pin to use as output. GPIOTE is not used for the pin. */
    #if defined __cplusplus
    #define GPIOTE_CONFIG_OUT_SIMPLE(init_high) nrf_drv_gpiote_out_config_t_struct(                    \
    											NRF_GPIOTE_POLARITY_LOTOHI,                            \
                                                init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW, \
                                                false )
    #else
    #define GPIOTE_CONFIG_OUT_SIMPLE(init_high)                                                        \
        {                                                                                              \
            .init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW,    \
            .task_pin = false,                                                                         \
        }
    #endif

Related