I'm trying to do a macro like this:
#define STRING(i) \
struct STRING##i \
{ \
size_t len; \
char chars[i]; \
}
but the problem is this works with constexpr arguments like this:
constexpr int ten = 10;
STRING(ten) mystr;
I don't want that because then STRING(ten) and STRING(10) aren't compatible types and that might confuse users of this macro.
I tried the following:
#define STRING(i) \
struct STRING##i \
{ \
_Static_assert( CAT(i, ul) , "must be nonzero literal"); \
size_t len; \
char chars[i]; \
}
it appends ul to the literal and makes it an unsigned long literal, but fails with non-literals since it makes it a different identifier. but the problem with this is if the user has another constexpr named tenul for example.
I wonder if there's a better way to make this macro fail unless an integer literal is provided.
Edit
To add to the accepted answer, I did:
#define STRING(i) \
struct STRING##i \
{ \
_Static_assert((1##i##1ul || i##8ul || 1), "argument must be positive decimal integer literal"); \
size_t len; \
char chars[i]; \
}
This makes sure octals and hexadecimals aren't accepted, only decimal literals.
Valid identifiers start with alphabetic characters or underscores. Valid integer literals start with digits. If only there was a way to check if the first character of
#i(the macro argument converted to a string) was numeric at compile time. But as far as I can tell using(#i)[0]only works at runtime.The question is tagged c23 so here is the only method I could think of and only works in C23:
In 6.6 Constant Expressions:
This means in theory you should be able to have code along the lines of:
What this does is creates a constant anonymous union containing a
charfield and achar[]field and uses the.operator to get thecharfield which will be the first character of thechar[]field and then checks if the character is numeric.Edit: The above technically invokes undefined behavior/is not required to compile as per the following rule:
Alternatively if you want a compile time error for a non literal integer value and do not mind losing the ability to use hexadecimal constants you could use:
If
iwere an identifier thenipreceded by a 1 would not be a valid identifier name but prepending 1 to an octal or decimal constant would still be a valid expression.Note:
1##i##0can also be used to block identifiers such aslorul.