0

I have the following pieces of code:

struct DataBase {
    union manip_types *manip;
};

union manip_types{
    void ( *man_insert )( struct DataBase *, struct auto_increment *, enum db_insert_types,... );
    void ( *man_remove )( struct DataBase *, struct auto_increment *, enum db_remove_types, int );
    void ( *man_update )( struct DataBase *, enum db_update_types, int,... );
    union db_query_union ( *man_query )( struct DataBase *, enum db_query_types, int );
    struct db_query_extended ( *man_query_ex )( struct DataBase *, struct auto_increment *, enum db_query_types,... )
};

And then i did:

db->manip = malloc( 5 * sizeof( union manip_types ) );
    db->manip[ 0 ].man_insert = &db_insert;
    db->manip[ 1 ].man_remove = &db_remove;
    db->manip[ 2 ].man_update = &db_update;
    db->manip[ 3 ].man_query = &db_query;
    db->manip[ 4 ].man_query_ex = &db_query_ex;

The code above generates the following warning for each [ 0, 1, -> 4 ]

Assignment from incompatible pointer type.

Function prototypes:

void db_insert( struct DataBase *db, struct auto_increment *a_i, enum db_insert_types db_insert_type,... )
void db_remove( struct DataBase *db, struct auto_increment *a_i, enum db_remove_types db_remove_type, int removeId )
void db_update( struct DataBase *db, enum db_update_types db_update_type, int upId,... )
union db_query_union db_query( struct DataBase *db, enum db_query_types db_query_type, int queryId )
struct db_query_extended db_query_ex( struct DataBase *db, struct auto_increment *a_i, enum db_query_types db_query_type,... )

What should i modify? And how could i call that array of pointers ? For example, v[ 4 ].

Adi Pîslaru
  • 139
  • 2
  • 13
  • Just out of curiosity, *why* would you use an array of unions like this, instead of a single struct containing five function pointers of appropriate type? – Ilmari Karonen Dec 02 '16 at 16:12
  • because i was requested to make an ARRAY of POINTERS and i don't know how to do it without unions because my function has different types( 3 voids, 1 struct and 1 union )... – Adi Pîslaru Dec 02 '16 at 16:16
  • 2 remarks: 1. any type of pointer (including function pointers) can be converted to a `void *` and back again, so you could use a `void * manip[5]`. 2. I cannot reproduce the warning - are the function prototype declared before the assignment? – Serge Ballesta Dec 02 '16 at 16:32
  • Yes, they are. CAn you give me an example on how to write my code in the manner of void *manip[5]? – Adi Pîslaru Dec 02 '16 at 16:33
  • You could cast all the pointers to the same type (which, to be fully portable, [should be a function pointer type of some kind](http://stackoverflow.com/questions/12358843/why-are-function-pointers-and-data-pointers-incompatible-in-c-c), such as `void(*)()`, although any POSIX-compliant system will let you use a plain `void *` too), and cast them back before calling them. – Ilmari Karonen Dec 02 '16 at 16:33
  • When should i do the casting work ? In which function ? A little example would be so helpfull.. for db_query cast to void and back – Adi Pîslaru Dec 02 '16 at 16:47

1 Answers1

1

Instead of trying to use an array of unions like that, you could just declare an array of generic function pointers, like:

void (*manip[5])();

or, if you want to allocate the array dynamically:

void (**manip)() = malloc(5 * sizeof( void(*)() ));

and cast all your function pointers to the generic type void(*)():

manip[0] = (void(*)()) &db_insert;
manip[1] = (void(*)()) &db_remove;
manip[2] = (void(*)()) &db_update;
manip[3] = (void(*)()) &db_query;
manip[4] = (void(*)()) &db_query_ex;

Of course, to call these functions, you'd need to cast them back into their proper types, like:

void (*update)(struct DataBase *, enum db_update_types, int, ...)
   = (void (*)(struct DataBase *, enum db_update_types, int, ...)) manip[2];
update(db, type, n, etc);

For the sake of readability and maintainability, you may want to typedef those function signatures into something more readable, like:

typedef void ( *man_insert_t )( struct DataBase *, struct auto_increment *, enum db_insert_types,... );
typedef void ( *man_remove_t )( struct DataBase *, struct auto_increment *, enum db_remove_types, int );
typedef void ( *man_update_t )( struct DataBase *, enum db_update_types, int,... );
typedef union db_query_union ( *man_query_t )( struct DataBase *, enum db_query_types, int );
typedef struct db_query_extended ( *man_query_ex_t )( struct DataBase *, struct auto_increment *, enum db_query_types,... );

allowing you to simply write

man_update_t update = (man_update_t) manip[2];
update(db, type, n, etc);

or even just:

((man_update_t) manip[2])(db, type, n, etc);

Of course, in practice, the proper solution would be to replace the array with a struct, letting you avoid all these type casting tricks:

struct db_manip {
    void ( *insert )( struct DataBase *, struct auto_increment *, enum db_insert_types,... );
    void ( *remove )( struct DataBase *, struct auto_increment *, enum db_remove_types, int );
    void ( *update )( struct DataBase *, enum db_update_types, int,... );
    union db_query_union ( *query )( struct DataBase *, enum db_query_types, int );
    struct db_query_extended ( *query_ex )( struct DataBase *, struct auto_increment *, enum db_query_types,... );
};

struct db_manip *manip = malloc(sizeof(struct db_manip));
manip->insert = &db_insert;
manip->remove = &db_remove;
manip->update = &db_update;
manip->query = &db_query;
manip->query_ex = &db_query_ex;
Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153