0

I have specific data types:

typedef struct _A_s { ... } A_t;
typedef struct _B_s { ... } B_t;

I also have specific functions to operate on these (e.g. A_t * init_A(); free_A(A_t *); print(A_t *); etc.

A 3rd function takes in any of those data struct types via a void * and also function pointers in order to free and print its input. I want to keep this function generic, hence converting A_t* to void*. But I am not sure how that will work with function pointers.

Here is a rough (sorry!) sketch:

typedef (void )(*free_generic_t)(void *);
typedef (void )(*print_generic_t)(void *);
void myfunc(
  void *object,
  free_generic_t free_fn,
  print_generic_t print_fn
){
  ...
  print_fn(object);
  free_fn(object);
}

My aim is to do this:

A_t *a = init_A(...);
B_t *b = init_B(...);
myfunc((void *)a, free_A, print_A);
myfunc((void *)b, free_B, print_B);

but gcc complains because of -Werror=incompatible-pointer-types, which I want to keep on. Ignoring this warning, the program works fine.

When I did this typecast I got a SIGSEGV it worked fine and passed the valgrind test:

myfunc(
  (void *)a,
  (free_generic_t )free_A,
  (free_generic_t )print_A
);

Question 1: How far can I go in typecasting functions to function pointers in the above scenario? Assume that the number of input parameters is fixed. That is, all print_A()'s take just 1 argument in, which will be a pointer.

Question 2: The problem becomes more complex when some of those specific functions return int and some nothing (void). Can I adjust the generic function pointers to return int in order to accommodate both flavours of specific functions even if some of them return void?

Update: I don't get a SIGSEGV when I typecast the function pointers, it was a mistake of mine. I have edited my post in place.

bliako
  • 977
  • 1
  • 5
  • 16
  • C is not the best language for such concepts. You might get away to some degree if you use unions that "combine" all your types, for arguments and return values. But it results in code not quite comprehensible. -- Did you look into `_Generic`? -- Consider using a programming language with appropriate concepts. If you badly want something near to C, try C++, but it has a steep learning curve. – the busybee Sep 30 '21 at 09:43
  • 1
    C is perfectly capable of modelling such an interfaces. Please read my answer to a similar question https://stackoverflow.com/questions/66450302/making-two-objects-in-c-more-object-oriented/66461232#66461232 – tstanisl Sep 30 '21 at 10:07
  • I chose C for a very good reason, apart from being my alma mater, it is lightning fast. IMO the best candidate for real-time, internet applications... – bliako Sep 30 '21 at 10:46
  • @tstanisl I will study that thanks – bliako Sep 30 '21 at 10:48
  • 1
    Try `typedef (void )(*free_generic_t)(/* no prototype here */);`. – chux - Reinstate Monica Sep 30 '21 at 12:13

1 Answers1

1

Give those structs

typedef struct _A_s { ... } A_t;
typedef struct _B_s { ... } B_t;

an pointer to a deallocator function and populate it in the A_t * init_A(); respectively B_t * init_B();.

Then you may write a generic deallocator for both of them which indirectly calls the deallocator via the stored pointer.

// Anything, which is shared between structures
typedef struct _X_s { void (*deallocator)(void *); ... } X_t;

typedef struct _A_s { X_t x; ... } A_t;
typedef struct _B_s { X_t x; ... } B_t;

Then

void deallocate(X_t *x){
    x -> deallocator(x);
}

int main(void){
    A_t *a = init_A();
    B_t *b = init_B();

    deallocate((X_t *)a);
    deallocate((X_t *)b);
}

Please be aware that this is only a concept of how you can do it. You must work out the details by yourself and add validity checks and such.

  • 1
    Note that inlining struct members of the other structure via "anonymous struct" is an extension to C enabled by `-fms-extensions` flag – tstanisl Sep 30 '21 at 13:51
  • 1
    https://stackoverflow.com/questions/8932707/when-are-anonymous-structs-and-unions-useful-in-c11/56147884#56147884 – Wör Du Schnaffzig Oct 01 '21 at 07:03
  • 1
    No, anonymous structures do not work this way. You must embed `struct { ... }` into the other struct to make it work. Your code does not even compile without "-fms-extensions" enabled.See https://godbolt.org/z/e63d3Eqab. Please add note that this solution requires the mentioned extension. – tstanisl Oct 01 '21 at 07:54
  • 1
    Alternatively you can replace `X_t;` with `X_t x;`. This should work because a pointer to a struct can be interchanged with the pointer to its first member – tstanisl Oct 01 '21 at 07:59
  • As this is a separate diskussion and the point i wanted to show does not need any anonymous structs i remove them from my answer. Sorry. – Wör Du Schnaffzig Oct 01 '21 at 08:11
  • So, ```X_t x``` should be the FIRST item in the struct and the reason is what @tstanisl said ```a pointer to a struct can be interchanged with the pointer to its first member``` – bliako Oct 06 '21 at 16:37