Path: ...!weretis.net!feeder9.news.weretis.net!i2pn.org!i2pn2.org!.POSTED!not-for-mail From: Richard Damon Newsgroups: comp.lang.c Subject: Re: Is it possible to generate a compile time error from an inline function? Date: Sat, 13 Jul 2024 14:07:58 -0400 Organization: i2pn2 (i2pn.org) Message-ID: References: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Injection-Date: Sat, 13 Jul 2024 18:07:58 -0000 (UTC) Injection-Info: i2pn2.org; logging-data="3137773"; mail-complaints-to="usenet@i2pn2.org"; posting-account="diqKR1lalukngNWEqoq9/uFtbkm5U+w3w6FQ0yesrXg"; User-Agent: Mozilla Thunderbird X-Spam-Checker-Version: SpamAssassin 4.0.0 In-Reply-To: Content-Language: en-US Bytes: 5897 Lines: 115 On 7/13/24 1:56 PM, Alan Mackenzie wrote: > Hello, Richard. > > Richard Damon wrote: >> On 7/13/24 1:05 PM, Alan Mackenzie wrote: >>> Hello, David. > >>> Many thanks for the reply! It's just what I was looking for. > >>> David Brown wrote: >>>> On 13/07/2024 15:04, Alan Mackenzie wrote: >>>>> Hello, comp.lang.c. > >>>>> What I want to do is check the validity of (constant) arguments to an >>>>> inline function, and output a compiler error if they are invalid. > >>>>> In particular, I have: > >>>>> u32 __always_inline ACM_BITFIELD (u8 a[], int offset, int length) > >>>>> , which is to extract a bitfield of LENGTH bits, starting at bit number >>>>> OFFSET in the array of bytes A. OFFSET and LENGTH will be known at >>>>> compile time. > >>>>> For the sake of run time efficiency, I wish to impose the restrictions >>>>> that either (i) the bitfield will be contained entirely within a byte; or >>>>> (ii) the bitfield will be a number of consecutive whole bytes (maximum 32 >>>>> bits). > >>>>> So, for example, if the code called > >>>>> foo = ACM_BITFIELD (bar, 14, 4); > >>>>> , I would like to output the compiler message "Invalid arguments 14, 4, >>>>> to ACM_BITFIELD", since this bitfield straddles two bytes. > >>>>> Is there any way I can do this in C? (Before anybody asks, yes I have >>>>> looked at doing it with macros, but that seems impractical, if it's even >>>>> possible.) > >>>>> Thanks! > > >>>> C does not have a way to force checks for this kind of thing at compile >>>> time. But if you are using an optimising compiler, you can perhaps rely >>>> on dead-code elimination along with link-time checks. > >>>> For example, declare a function "compile_time_error()" but do not define >>>> it anywhere. Then add a check : > >>>> if (length > 8) compile_time_error(); > >>>> As long as your check is not too complicated in relation to your >>>> compiler's optimisation abilities, if it knows the value of "length" at >>>> compile time it and can see it is no more than 8, the call to >>>> compile_time_error() will be eliminated. If not, the call will be kept >>>> and your link will fail as the function does not exist. > >>> This might be the way to go. The number of erroneous calls to >>> ACM_BITFIELD is expected to be low. The check is just there to make it >>> difficult for the optimisations in the function to create nonsense. But >>> if there are ~30 calls to the function, it would then be difficult to >>> located the erroneous one. So, perhaps .... > >>>> If you are using gcc (or, I expect, clang), you can go further. > >>>> First, define the compile_time_error() function with an error attribute: > >>>> extern void __attribute__((error("Compile time error"))) >>>> compile_time_error(void); > >>>> Then if the call is not eliminated, you will get an error message at >>>> compile time rather than waiting for link time. > >>>> Add a "__attribute__((always_inline))" attribute to your bitfield >>>> function - if it is used in a context that is not inlined, that will >>>> show up as an error. > >>>> You might also find the gcc built-in function __builtin_constant_p(...) >>>> useful to determine if an expression value is known at compile time, in >>>> case you want to add run-time checking for complex cases. > >>> Thanks, these are all things I didn't know. I will look them up in the >>> GCC manual. :-) > > >> In C++, I would use constexpr and static_assert to do this, and your >> compiler might allow its use in C as an extension. > > C++ isn't an option. The question being since most C compilers are also C++ compilers, they somethings accept this sort of C++ism as an extension. > >> If not, in C you could use just _Static_assert, perhaps in the expansion >> of a macro that generates the expression that does the testing. > > Yes, that's an even better idea, thanks. Looking it up in > https://en.cppreference.com, _Static_assert has existed in C since C11, > that spelling being deprecated in favour of static_assert in C23. I just > need to check the project I'm working in doesn't still support C < C11. > If the compiler doesn't support some form of static assert, you cam make one yourself with a macro. #define static_assert(x) extern int _static_assert[(x) ? 1 : -1] Possible adding whatever hacks you want to make the variable unique, (or make it a typedef, or whatever). (a typedef doesn't add an external symbol that is never used, so might be clearer, but needs something to make the name unique) The key idea is a array of negative size is a constraint error, so if x is false you get the error.