3

I have a function with a prototype like this:

ErrorType function(void ** parameter, other_args);

This function reads the pointer pointed by 'parameter' and changes it (think of it like a realloc).

Now, to be right according to the C Standard, if I want to pass the address of other pointer than a void *, I must declare a temporary void * variable and use that instead.

So that I want is to create a wrapper (I don't care if it's a function or a macro), that do the function call with any pointer type.

I think I could do that in C11 with _Generic and a function for each basic type, plus a function for all structs and a function for all unions, but I think it's too troublesome.

I also read about a GCC extension that let you to write statements and declarations in expressions, and I think that I can easily do that I want with that, but I prefer that my code compiles in all standard compilers, not only in GCC or Clang.

So the question is, is there any way to do that without too much problems in a C11 compiler?

Mabus
  • 1,418
  • 12
  • 20
  • Cannot find a way without persistent storage for the return-type. If we have scratch-space for that, it's easy. – Deduplicator Aug 19 '14 at 16:09
  • I also though about TLS and it could be a 'hack' to do that. But AFAIK TLS have two problems: it can be a limited resource, and it could have reentrancy problems in presence of signals. – Mabus Aug 19 '14 at 16:12
  • Why do you think the C standard requires such a cast? I believe casts to void and back are OK. Are you getting a warning of some kind? As many libraries do this everywhere and do not cause issues, this purpose is basically what void is there for... – Vality Aug 19 '14 at 16:27
  • @Vality: E.g. casting a `double **` to `void **` is OK, passing to `function` also is, but dereferencing is a violation of strict aliasing (accesses a `double *` object with an lvalue of type `void *`). Additionally, there may be problems with different representations for `void *` and `double *` (but I consider this to be not an issue on common platforms). – mafso Aug 19 '14 at 16:30
  • Conversions between any pointer type and void* are OK and a cast isn't needed (in C). But here there is a void** so you can't assign a double** (even with a cast, I think, at least is unportable). – Mabus Aug 19 '14 at 16:31
  • @mafso However, what one would normally do would be to cast to char **. The standard guarantees a char * can alias any other pointer safely. – Vality Aug 19 '14 at 16:32
  • @Vality: You can access everything through an lvalue of a character type, yes, but not through one of pointer-to-character type. – mafso Aug 19 '14 at 16:33
  • @mafso Hmn... Looking into it I can see one safe way of doing this which is to memcpy your pointer over the other one, memcpy is guaranteed to be safe as far as aliasing goes despite the cast – Vality Aug 19 '14 at 16:41
  • @Vality You suppose that all pointers have the same size, but the C Standard doesn't say so. – Mabus Aug 19 '14 at 16:45
  • @Vality additionally to the sizes and representation and alignment requirements, `memcpy`ed objects "remember" their original type (the _effective type_ in the standard), so strict aliasing is still a problem. I don't think this discussion leads to an answer to the question here, so if you're interested, let's continue in chat. – mafso Aug 19 '14 at 16:55
  • 2
    The function approach is discussed e.g. [here](http://stackoverflow.com/questions/24002093) (with the conclusion, that there's no strictly conforming way), the macro approach would work if you pass in the error variable (which also can be returned), static/TLS variables (as discussed above), or as said in the question with expression statements. I don't know another way. – mafso Aug 19 '14 at 17:00

1 Answers1

0

If I understand the question correctly, you'd like for function to be able to modify various types of pointers. Well, there's bad news and good news about that.

Bad news: The object representation of pointers is opaque, so you'd need to communicate to your function which kind of pointer it should be working with, unless your function is guaranteed to copy an object representation from a source pointer's object representation and you know that the two representations have the same meaning.

For example, sizeof (double *) could be different than sizeof (void *)

Good news: A pointer to any object type can be cast to a void * and back again, which includes a void ** and a double ** any many other pointer-types. So you could have:

ErrorType function(void * ptr, int ptr_type, ...) {
    void ** vpp;
    double ** dpp;
    ...

    ...
    switch (ptr_type) {
        case PTR_TYPE_VOIDP:
        vpp = ptr;
        /* Now you can work with *vpp */
        *vpp = ...
        break;

        case PTR_TYPE_DOUBLEP:
        dpp = ptr;
        /* Now you can work with *dpp */
        *dpp = ...
        break;
      }
    ...
  }
Shao
  • 537
  • 3
  • 7
  • But that is a lot of work. I would have to do different code for each type, and in C there are a lot of different types. – Mabus Aug 29 '14 at 07:19
  • 1
    Agreed. On some systems, a pointer is a pointer is a pointer and they are all the same. But C doesn't encourage that and for portable code, each pointer-type will have to be dealt with. `char *`, `unsigned char *`, `signed char *` and `void *` all have the same representation and alignment, so you can handle all of those, together. Any `struct foo *` will be the same as any other `struct bar *`. Any `union foo *` will be the same as any other `union bar *`. But the rest are unspecified. – Shao Aug 30 '14 at 13:27