1

Here is a MRE

#include <string.h>

typedef char* mytype;

typedef int(*cmp)(const mytype, const mytype);

void foo(cmp f) {}

int main(void) {
    foo(strcmp);
}

When I compile I get:

$ gcc mre.c -Wall -Wextra
mre.c: In function ‘foo’:
mre.c:7:14: warning: unused parameter ‘f’ [-Wunused-parameter]
    7 | void foo(cmp f) {}
      |          ~~~~^
mre.c: In function ‘main’:
mre.c:10:9: warning: passing argument 1 of ‘foo’ from incompatible pointer type [-Wincompatible-pointer-types]
   10 |     foo(strcmp);
      |         ^~~~~~
      |         |
      |         int (*)(const char *, const char *)
mre.c:7:14: note: expected ‘cmp’ {aka ‘int (*)(char * const,  char * const)’} but argument is of type ‘int (*)(const char *, const char *)’
    7 | void foo(cmp f) {}
      |          ~~~~^

The first warning is irrelevant. But what do I do about the second? I tried changing the typedef to:

typedef int(*cmp)(mytype const,mytype const);

But that gave the exact same result. When I changed to:

typedef int(*cmp)(const char*, const char*);

it worked, but that's not preferable for obvious reasons. Another thing that worked but also is not preferable is this:

typedef const char* mytype;

typedef int(*cmp)(mytype, mytype);

So what have I missed here?

The problem I'm trying to solve is that I want to create a generic structure where the data is of type mytype. The user of the structure is expected to implement a compare function, but I want it to work with strcmp in case mytype is of type char*.

Note that mytype not necessarily needs to be a pointer. It depends on what data type the user specifies. Also, I know that typedefing pointers is bad practice in general, but I consider this a special case because I want to typedef whatever the type is, and it may be a pointer.

The structure looks like this:

struct node {
    struct node *next;
    mytype data;
};

I managed to solve it with #define mytype char* but that feels very ugly, and I would prefer if there was another way. I want it to be portable, so I don't want to use gcc extensions.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • What you missed: https://stackoverflow.com/questions/750178/is-it-a-good-idea-to-typedef-pointers – user3386109 Nov 07 '20 at 23:04
  • Does this answer your question? [typedef pointer const weirdness](https://stackoverflow.com/questions/8504411/typedef-pointer-const-weirdness) – max Nov 07 '20 at 23:11
  • 2
    The typedef for `mytype` should be `typedef char mytype;` so that `mytype` is actually a type, and not a pointer to a type. Then the typedef for the function is `typedef int (*cmp)(const mytype *, const mytype *);` When `mytype` is just a type, you have full control over the ordering of the decorators. – user3386109 Nov 07 '20 at 23:12
  • In response to the edit: it seems like there's a bigger picture that you haven't shared with us. In other words, this is beginning to look like an XY problem. – user3386109 Nov 07 '20 at 23:15
  • @user3386109 He wants polymorphism and templates. My opinion is that is you want those features program in C++ instead. – 0___________ Nov 07 '20 at 23:19
  • @P__JsupportswomeninPoland Well, if it wasn't for the fact that `mytype` may be a pointer, then it would have worked. – klutt Nov 07 '20 at 23:23
  • klutt, Given "generic structure where the data is of type `mytype`", if the type was not a pointer, you seem to want `mytype` as a `const` function parameter, but if `mytype is a pointer, you do not `mytype` as a `const` function parameter, but as a non-`const` type that points to `const` data. Is that right? – chux - Reinstate Monica Nov 08 '20 at 04:27
  • @chux-ReinstateMonica Yes, that is correct. – klutt Nov 08 '20 at 14:37
  • klutt, If `mytype` was already pointing to `const` data as with `typedef const char* mytype;`, how do you want to reconcile a goal of somehow handling `const mytype`? As `const const char *`? – chux - Reinstate Monica Nov 08 '20 at 14:51
  • @chux-ReinstateMonica I'm aware of those problems. I'm looking for a solution, but it does not seem to be a good one. The two viable alternatives I have come up with so far is either using `#define` or changing `data` to `void *` and redesign everything. Or using gcc extensions, which I don't want to do. – klutt Nov 08 '20 at 14:54
  • Perhaps a 3rd choice? The true problem is lightly described in "The problem I'm trying to solve..." and this post has a non-working approach where a fix is attempted. I'd rather know more of the true issue to best propose a working solution. Perhaps along this lines of making 2 types: `typedef char* mytype;` and `typedef const char* myconsttype;` and then `typedef int(*cmp)(myconsttype, myconsttype);` – chux - Reinstate Monica Nov 08 '20 at 15:04
  • @chux-ReinstateMonica That's also a solution I have thought of. What I don't like about it is that it requires the user of the library to configure two things instead of just one. – klutt Nov 08 '20 at 16:01
  • @klutt You are already requiring 2 things of the user: `typedef char* mytype;` and saying `strcmp()` is OK to use it. Maybe with `typedef char* mytype;` and oblige the user to supply a compare function: `int mystrcmp(mytype a, mytype b);` which wraps `strcmp()`. – chux - Reinstate Monica Nov 08 '20 at 16:47
  • @chux-ReinstateMonica I'm grateful for your help, but I think I leave it as it is. You're giving viable options, but unfortunately, I only have a couple of half bad solutions but none that seems good. If it was C++, the obvious answer would be templates. – klutt Nov 08 '20 at 17:00
  • 1
    I think I'd go with `foo((cmp)strcmp);` – chux - Reinstate Monica Nov 08 '20 at 17:05
  • @chux-ReinstateMonica I was tempted, but what if the user gives a non compatible function? Then I would like to at least get a warning. – klutt Nov 08 '20 at 17:09
  • With simply types, no need for user to even supply `strcmp()` as `_Generic` can select a proper compare function. For complex types, trust the user. – chux - Reinstate Monica Nov 08 '20 at 17:17
  • 1
    suggest you examine the source for the `qsort()` function as that requires the user to write the compare function. – user3629249 Nov 09 '20 at 15:31

1 Answers1

0
typedef char* mytype;

mytype is a pointer to char. const mytype is a const pointer to char not pointer to const char.

Unfortunately in C you cant use typedef pointers and the use this type to declare pointer to const object.

you need to

typedef int(*cmp)(const char *, const char *);

void foo(cmp f) {}

int main(void) {
    foo(strcmp);
}

or

    foo((cmp)strcmp);

BTW it a very bad practice to hide pointers behind the typedefs.

0___________
  • 60,014
  • 4
  • 34
  • 74