coreMQTT - How to pass user data to EventCallback?

Hello all,

We are using coreMQTT library on a C++ project and we need to pass the this pointer of a class to the callback. On the api for v3.1.1 I didn’t see any user parameters in EventCallback function.

We were using coreMQTTAgent before which allows us to pass parameters to callbacks, however we are not able to use that library since we need to access the cell modem when we are running the Agent and this would create concurrency issues. We don’t want to implement locks around Cellular Interface since that would defeat the purpose of an Agent based design and might even lead to priority issues. We really want to avoid globals and singletons so I am open to suggestions on how to achieve this without using those.

Thank you,
Tuna Bicim

I do not think it is directly possible but can you use a workaround like the following -

/* Declare a struct with MQTTContext_t as the first member.
 * It is important to keep MQTTContext_t as the first member. */
typedef struct MQTTExtendedContext
{
    MQTTContext_t ctx;
    void* pUserData;
} MQTTExtendedContext_t;

MQTTExtendedContext_t extendedCtx = { 0 };

/* Initialize pUserData to whatever you want (likely this pointer
 * in your case). */
extendedCtx.pUserData = this;

/* Use the ctx member of the MQTTExtendedContext_t when interacting
 * with the coreMQTT APIs. */
MQTT_Init( &( extendedCtx.ctx ), ... );
MQTT_Connect( &( extendedCtx.ctx ), ... );

/* Cast MQTTContext_t back to MQTTExtendedContext_t in the event
 * callback and access pUserData. This cast is only possible
 * when MQTTContext_t is the first member of MQTTExtendedContext_t. */
void MQTTEventCallback( MQTTContext_t * pMQTTContext,
                        MQTTPacketInfo_t * pPacketInfo,
                        MQTTDeserializedInfo_t * pDeserializedInfo )
{
    MQTTExtendedContext_t * pMQTTExtendedContext = ( MQTTExtendedContext_t * ) pMQTTContext;

    /* You can now access pMQTTExtendedContext->pUserData. */
}

Hi Gaurav,

Thanks for the suggestion, it should definitely work.
I didn’t think about the classic C OOP Inheritance hack :smiley:

Is it possible to make a feature request here to have a pUserData field to be added to the library or do I need to make a Github issue to do that?

Thank you,
Tuna

Hello @tbicim,

Yes, @aggarg is correct, there is no direct way to do this and the way he suggested should work.

To make your code ‘future-proof’, I suggest you utilize the offsetof function if your compiler supports it to get the pointer to MQTTExtendedContext_t from the given MQTTContext_t *.

You can go about it like so:

/* Cast MQTTContext_t back to MQTTExtendedContext_t in the event
 * callback and access pUserData. */
void MQTTEventCallback( MQTTContext_t * pMQTTContext,
                        MQTTPacketInfo_t * pPacketInfo,
                        MQTTDeserializedInfo_t * pDeserializedInfo )
{
    MQTTExtendedContext_t ExtendedCtx = { 0 };
    /* Calculate offset of the member from the structure base. */
    ptrdiff_t offset = ( ( uint8_t * ) &( ExtendedCtx .ctx) )
                     - ( ( uint8_t * ) &( ExtendedCtx ) );

    /* Get the Extended Context by moving the pointer back by offset. */
    MQTTExtendedContext_t * pMQTTExtendedContext = ( MQTTExtendedContext_t * ) &( ( ( uint8_t * ) ctx )[ 0 - offset ] );

    /* You can now access pMQTTExtendedContext->pUserData. */
}

This ensures that even if someone moves the MQTTContext_t ctx; around in the MQTTExtendedContext structure (or adds a member before it), you still get the correct pointer to MQTTExtendedContext_t which you can then dereference.

You can request that feature here, but a GitHub Issue helps us track the feature requests better and we can see which user created that and other users can add a comment saying that they also need it which allows us to gauge the interest of users in a given feature request.

Would you like to create a feature request on GitHub by following this link: New Issue · FreeRTOS/coreMQTT?

Hi Aniruddha,

Thanks for the suggestion. I think for now I will keep ctx to be the first member and add a static assert that checks offsetof context is always 0. That way I can catch if anyone adds a member beforehand. It is simple and self documenting :slight_smile:

Thank you for the link, I created a Github issue about it.

Thanks,
Tuna

1 Like