Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile-time assertion to determine if pointer is an array

Tags:

c

assertion

Currently, I have the following block of code to make safe string copying (it works):

#define STRCPY(dst, src) do { assert(((void*)(dst)) == ((void*) & (dst))); \
                              strlcpy(dst, src, sizeof(dst)); } while (0)

So it accepts the construction like:

const char *src = "hello";
char dest[5];
STRCPY(dest, src); //hell

And denies the following:

void (char *dst) {
    STRCPY(dst, "heaven"); //unknown size of dst
}

The problem is that the block of code creates an assertion. Is there a way to perform this check on compilation time?

So I want to have the error on compilation (like creating an array with negative size) instead of crashing code if it possible.

like image 704
Vladimir Botov Avatar asked Mar 05 '23 20:03

Vladimir Botov


1 Answers

If standard C is available, then you can do this:

#define STRCPY(dst, src)                                        \
  _Generic(&(dst),                                              \
           char(*)[sizeof(dst)]: strlcpy(dst,src,sizeof(dst)) )

Explanation:

You can't use a _Generic expression on an array type, because it is not one of the special cases that is exempt from the "array decay" rule (C17 6.3.2.1 §3). So by simply using _Generic((dst), ... in my example, dst would end up as a char* when the expression is evaluated, and then we would lose the information of its original type.

But if we take the address of the array using &, we do utilize one of those special cases and the array decay doesn't happen. Instead we end up with an array pointer, meaning that _Generic will have to check for an array pointer of the expected type and size: char(*)[sizeof(dst)].


As a side-note/safety concern, I never use do-while(0) macros and discourage them, but that's another story.

like image 184
Lundin Avatar answered Mar 10 '23 10:03

Lundin