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.
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. */
}
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.
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
Thank you for the link, I created a Github issue about it.