-1

I have defined interface:

typedef DataExchg_Status_T (*DataExchg_Iface_ReceiveDataClbk_T)(
        void * p_self, uint8_t const * p_data, size_t size);

typedef struct DataExchg_Iface_Tag {
    DataExchg_Status_T (*SendData)(void * p_self, uint8_t const * p_data, size_t size);
    void (*RegisterReceiveDataClbk)(void * p_self,
                                    DataExchg_Iface_ReceiveDataClbk_T receive_data_clbk,
                                    void * p_receiver_instance);
    void * pSelf;
} DataExchg_Iface_T;

and I use it in Object_A.

typedef struct Object_A_Tag {
     DataExchg_Iface_T * pIface;
} Object_A_T;

In Object_B module


I have implementation of this interface:

typedef struct Object_B_Tag {
     int value;
     ...
} Object_B_T;

DataExchg_Status_T SendData(Object_B_T * p_self, uint8_t const * p_data, size_t size) {
    ...
}

void RegisterReceiveDataClbk(Object_B_T * p_self,
                             DataExchg_Iface_ReceiveDataClbk_T receive_data_clbk,
                             void * p_receiver_instance) {
    ...
}

Everything is ok until I want to assign the above functions to the interface in Object_A:

Object_B object_b = {...};

Object_A object_a = {
    .pIface = &(DataExchg_Iface_T){
        .SendData = SendData,
        .RegisterReceiveDataClbk = RegisterReceiveDataClbk,
        .pSelf = &object_b
    }
};

The problem is that I get incompatible pointer warning because one of the parameter: void * p_self is not equal to Object_B_T * p_self in interface implementation.

There are some possible solutions of this problem:

1. Cast to the interface function pointer:

SendData = (DataExchg_Status_T(*)(void *, DataExchg_Iface_ReceiveDataClbk_T, void *)SendData

This is the most convenient solution, but there are some strong evidences that this solution could lead to undefined behaviour: Casting a function pointer to another type

2. Implement interface exactly as is declared and cast the parameter in the function body:

This is the safest solution, but not the most convenient.

DataExchg_Status_T SendData(void * p_self, uint8_t const * p_data, size_t size) {
    Object_B_T pSelf = p_self;
}

3. Cast to the interface function pointer when initialize interface (solution 1) and cast interface function to its implemented type every time when I use it:

This solution according to my knowledge should not lead to undefined behaviour.

object_b.pIface->(DataExchg_Status_T(*)(Object_B *, DataExchg_Iface_ReceiveDataClbk_T, void *)SendData(...)

And finally questions:

1) Can the solution 1 really lead to undefined behaviour in my case?

2) Is there any solution where I can have generic interface declaration with void instance pointer and specyfic implementation with specyfic instance pointer? (similar to solution 1)

Esato
  • 127
  • 1
  • 9

1 Answers1

0

Your second option, changing the function definition to match the pointer type and converting the void * to the proper type, it the correct and preferred way to implement a callback such as this.

As an example, take a look at the man page for qsort. The function is declared like this:

   void qsort(void *base, size_t nmemb, size_t size,
              int (*compar)(const void *, const void *));

And it gives an example of a callback function:

static int
cmpstringp(const void *p1, const void *p2)
{
    /* The actual arguments to this function are "pointers to
       pointers to char", but strcmp(3) arguments are "pointers
       to char", hence the following cast plus dereference */

   return strcmp(* (char * const *) p1, * (char * const *) p2);
}

As you can see, this example casts the void * to the correct type in order to use it. This is a common idiom with C callback functions.

dbush
  • 205,898
  • 23
  • 218
  • 273