Eric Sosman wrote:
> Bryan Crouse wrote On 09/04/07 10:07,:
>> I am looking a way to do error checking on a string at compile time,
>> and if the string isn't the correct length have then have the compiler
>> throw an error.
>>
>> I am working an embedded software that will require individual builds
>> for each device so that the device serial number is contained in the
>> program memory. To do this, the C application must be compiled with
>> the serial number assigned to a variable within the source code file.
>> I would like to provide compile time error checking within the .c file
>> if possible so that if the length of the string is not correct, then
>> the build process will fail and there is no risk of having an
>> executable that has a bad serial number.
>>
>> Has anyone heard of this or done this sort of thing? Any advice would
>> be greatly appreciated.
>
> Here's one horrid hack:
>
> char serial[] = "..."; /* should be 42 characters */
>
> /* If the following line produces an error, it means
> * that `serial' (above) does not have the expected
> * length. Pay no attention to the text of the error
> * message the compiler issues; the problem is with
> * the definition of `serial'.
> */
> static char fake[ (sizeof serial == 42 + 1) * 2 - 1 ];
>
> If the serial number is indeed 42 characters long (plus one
> for the trailing '\0'), fake[1] is a legal array declaration.
> If the length is something other than 42, you get fake[-1] and
> an error message.
>
> It seems to me, though, that you're attacking the problem
> at the wrong place. This hack can check the length and can
> maybe be extended to check a few other things, but it's not
> going to be easy (or maintainable) to get more thorough
> validation from it. If you've got a rule like "The first
> two characters are upper-case letters, followed by five
> digits and three letters or by six digits and two letters,
> followed by ..." then this technique will be far more trouble
> than it's worth. Instead, consider arranging your build
> procedure so the serial number gets validated by a program
> which then runs the build using that number. (For example,
> it might write the validated number to a small .c file that
> then gets compiled and linked in with the rest, or it might
> compile everything with a `-DSERIAL=AB1234ZX999' option, or
> something of that sort.) I think you'll find this more
> reliable than telling the builders "Edit the file serial.c
> and then rebuild the product. Be sure no one else is trying
> to build it at the same time ..."
>
I agree that -DSERIAL=AB1234ZX999 is the best ever (though only after a
post-link massaging of the const section/segment made by the linker alone).
Still, the usage of it may require validation in the C source.
In a similar case, I used
#define MYASSERT(cond) extern int assert_dummy[(cond)?1:-1]
#define SERIAL_ STRING(SERIAL)
( Of course: #define STRING(s) STRING_RAW(s) #define STRING_RAW(s) #s )
And now the useful part:
#define SERIAL_LEN (sizeof(SERIAL_)-1) //don't bother to store last '\0'
MYASSERT(SERIAL_LEN==WHAT_I_WANT);
char_or_perhaps__uint8_t unique_serial[SERIAL_LEN]=SERIAL_;
In a way, same Eric's (and, in a way, Jacob's) hack but draped enough to
look almost attractive
-- Ark