Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Porting Custom Service Example to C++

I've worked through the Custom BLE Service example (https://github.com/NordicPlayground/nRF5x-custom-ble-service-tutorial) and I'm attempting to port it to C++ to form the foundation of my own application. However, I've been immediately hit with errors when trying to compile as C++ as opposed to C.

I believe that the issue has something to do with the macro which defines a ble_cus instance

As all of the errors seem to relate to the compiler seeing m_cus as of type ble_cus_s as opposed to ble_cus_t. Is there an inherent difference as to how forward declaration works in C++ compared to C? Or am I missing something else here?

To replicate:

Take the program created by the tutorial (https://github.com/NordicPlayground/nRF5x-custom-ble-service-tutorial), change main.c to main.cpp


Parents
  • Update:
    I can get around this by surrounding #include "ble_cus.h" with an extern "C" statement, but this is a quick fix rather than a solution! I'm wanting to create a C++ class that can handle the BLE stack initialisation and callbacks, so at some point I'm going to have to reintegrate it...

  • Hi,

    TB-DaveMoore said:
    I can get around this by surrounding #include "ble_cus.h" with an extern "C" statement, but this is a quick fix rather than a solution!

     This is the solution. C++ mangles the names, while C does not. Therefore, any C functions and types included in a C++ project must be included as extern "C".

    You might be able to work around some of this by changing the file types from C to C++, thus hinting to the compiler that it should treat it all as C++, but the SDK is written in C, and at some point, you will have to handle the differences between C and C++.

    Best regards,

    Didrik

  • True, but as ble_cus_t/ble_cus_s are entirely custom types, surely there is a way of implementing a custom service as a C++ class instead of as a C struct? If we were to treat the components of the struct and associated callbacks as members of the class, we can encapsulate it and create objects for each of our custom characteristics with relative ease?

    The main thing that jumps out to me as an issue with this method is the semi-black box nature of the BLE_CUS_DEF macro - what is that macro actually doing and how can it be replicated for in a C++ class initialiser?

Reply
  • True, but as ble_cus_t/ble_cus_s are entirely custom types, surely there is a way of implementing a custom service as a C++ class instead of as a C struct? If we were to treat the components of the struct and associated callbacks as members of the class, we can encapsulate it and create objects for each of our custom characteristics with relative ease?

    The main thing that jumps out to me as an issue with this method is the semi-black box nature of the BLE_CUS_DEF macro - what is that macro actually doing and how can it be replicated for in a C++ class initialiser?

Children
  • OK, I think I understand more what you want now.

    I had talk with one of my colleagues, and it should be possible to implement a service as a C++ class, as at least the SoftDevice does not have any problems with C++.

    However, some of the modules in the SDK might provide some issues, especially if you use the macros.

    One thing that often has been a  problem is the way they initialize structs statically.

    Looking at the implementation of the NRF_SDH_BLE_OBSERVER macro, we run into this exact issue:

    /**@brief   Macro for registering @ref nrf_sdh_soc_evt_observer_t. Modules that want to be
     *          notified about SoC events must register the handler using this macro.
     *
     * @details This macro places the observer in a section named "sdh_soc_observers".
     *
     * @param[in]   _name       Observer name.
     * @param[in]   _prio       Priority of the observer event handler.
     *                          The smaller the number, the higher the priority.
     * @param[in]   _handler    BLE event handler.
     * @param[in]   _context    Parameter to the event handler.
     * @hideinitializer
     */
    #define NRF_SDH_BLE_OBSERVER(_name, _prio, _handler, _context)                                      \
    STATIC_ASSERT(NRF_SDH_BLE_ENABLED, "NRF_SDH_BLE_ENABLED not set!");                                 \
    STATIC_ASSERT(_prio < NRF_SDH_BLE_OBSERVER_PRIO_LEVELS, "Priority level unavailable.");             \
    NRF_SECTION_SET_ITEM_REGISTER(sdh_ble_observers, _prio, static nrf_sdh_ble_evt_observer_t _name) =  \
    {                                                                                                   \
        .handler   = _handler,                                                                          \
        .p_context = _context                                                                           \
    }

    One way to work around this issue would be to implement an alternative version of the macro. My colleague proposed this:

    NRF_SECTION_SET_ITEM_REGISTER(sdh_ble_observers, _prio, static nrf_sdh_ble_evt_observer_t _name) = \
    { \
      _handler, \
      _context \
    }

    The _context parameter should not be used by the SDK, so it should work to put a pointer-to-object there instead of pointer-to-struct.

Related