Discussion:
Is it possible to generate a compile time error from an inline function?
(too old to reply)
Alan Mackenzie
2024-07-13 13:04:04 UTC
Permalink
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!
--
Alan Mackenzie (Nuremberg, Germany).
David Brown
2024-07-13 15:15:06 UTC
Permalink
Post by Alan Mackenzie
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.
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.


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.
Alan Mackenzie
2024-07-13 17:05:22 UTC
Permalink
Hello, David.

Many thanks for the reply! It's just what I was looking for.
Post by David Brown
Post by Alan Mackenzie
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.
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
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 ....
Post by David Brown
If you are using gcc (or, I expect, clang), you can go further.
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. :-)
--
Alan Mackenzie (Nuremberg, Germany).
Richard Damon
2024-07-13 17:34:35 UTC
Permalink
Post by Alan Mackenzie
Hello, David.
Many thanks for the reply! It's just what I was looking for.
Post by David Brown
Post by Alan Mackenzie
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.
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
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 ....
Post by David Brown
If you are using gcc (or, I expect, clang), you can go further.
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.

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.
Alan Mackenzie
2024-07-13 17:56:15 UTC
Permalink
Hello, Richard.
Post by Richard Damon
Post by Alan Mackenzie
Hello, David.
Many thanks for the reply! It's just what I was looking for.
Post by David Brown
Post by Alan Mackenzie
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.
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
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 ....
Post by David Brown
If you are using gcc (or, I expect, clang), you can go further.
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.
Post by Richard Damon
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.
--
Alan Mackenzie (Nuremberg, Germany).
Richard Damon
2024-07-13 18:07:58 UTC
Permalink
Post by Alan Mackenzie
Hello, Richard.
Post by Richard Damon
Post by Alan Mackenzie
Hello, David.
Many thanks for the reply! It's just what I was looking for.
Post by David Brown
Post by Alan Mackenzie
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.
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
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 ....
Post by David Brown
If you are using gcc (or, I expect, clang), you can go further.
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.
Post by Alan Mackenzie
Post by Richard Damon
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.
Kenny McCormack
2024-07-14 06:30:29 UTC
Permalink
In article <***@i2pn2.org>,
Richard Damon <***@damon-family.org> wrote:
...
Post by Richard Damon
Post by Alan Mackenzie
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.
Yes, but this is comp.lang.c, where we spit on the idea of even granting
the existence of so-called "extensions" (much less actually using them).
--
Pensacola - the thinking man's drink.
David Brown
2024-07-14 13:17:35 UTC
Permalink
Post by Richard Damon
Post by Alan Mackenzie
Hello, Richard.
Post by Richard Damon
Post by Alan Mackenzie
Hello, David.
Many thanks for the reply!  It's just what I was looking for.
Post by Alan Mackenzie
Hello, comp.lang.c.
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.
Actually, the vast majority of C compilers are /not/ also C++ compilers.
But the most popular C compilers, by a significant margin, are C++
compilers.

_Static_assert has been in C since C11, so it does not need to be an
extension - it has been standard C for over a decade. (And many C
compilers allow as extensions the use of features from later C standards
to be used along with earlier standards, as well as - as you say -
allowing some C++-isms.) But to make _Static_assert work here, you'd
have to stretch things a lot further than just supporting C11/C++11
static assertions. Static assertions are a feature I like and find very
useful, but they don't work in all situations I would like.
Post by Richard Damon
Post by Alan Mackenzie
Post by Richard Damon
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.
That's a useful technique for those stuck with pre-C11 or pre-C++11 and
who don't want to use gcc-isms - I have such a macro in an oft-used header.
Keith Thompson
2024-07-14 19:14:10 UTC
Permalink
[...]
Post by David Brown
Post by Richard Damon
The question being since most C compilers are also C++ compilers,
they somethings accept this sort of C++ism as an extension.
Actually, the vast majority of C compilers are /not/ also C++
compilers. But the most popular C compilers, by a significant
margin, are C++ compilers.
I wouldn't quite put it that way.

gcc, clang, and Microsoft provide both C and C++ compilers, but they're
separate applications, perhaps invoked from a common driver program.
They typically share the same backend, but the frontends are separate.
I wouldn't expect to see any code that parses "int class;" and decides
whether it's a syntax error based on which language is being compiled.

The C and C++ frontends might share some code, but they're separate
applications.

[...]
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
David Brown
2024-07-14 20:50:38 UTC
Permalink
Post by Keith Thompson
[...]
Post by David Brown
Post by Richard Damon
The question being since most C compilers are also C++ compilers,
they somethings accept this sort of C++ism as an extension.
Actually, the vast majority of C compilers are /not/ also C++
compilers. But the most popular C compilers, by a significant
margin, are C++ compilers.
I wouldn't quite put it that way.
gcc, clang, and Microsoft provide both C and C++ compilers, but they're
separate applications, perhaps invoked from a common driver program.
They typically share the same backend, but the frontends are separate.
I wouldn't expect to see any code that parses "int class;" and decides
whether it's a syntax error based on which language is being compiled.
The C and C++ frontends might share some code, but they're separate
applications.
That is a lot more accurate than the way I described it.

However, it is the case that the C compilers have historically picked up
features from their sibling C++ compilers as extensions. They don't get
things that are directly counter to the way C works, and the good
features have often made it into later C standards.

So yes, separate applications but with some code sharing is a better way
to put it.
David Brown
2024-07-14 13:05:16 UTC
Permalink
Post by Richard Damon
Post by Alan Mackenzie
Hello, David.
Many thanks for the reply!  It's just what I was looking for.
Post by David Brown
Post by Alan Mackenzie
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.
      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
        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 ....
Post by David Brown
If you are using gcc (or, I expect, clang), you can go further.
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.
With C++, there are a few other options, including constexpr functions,
"if consteval", std::is_constant_evaluated, and other bits and pieces
that could be used to check that the function is used with known value
of "length".
Post by Richard Damon
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.
Unfortunately, _Static_assert could not be used here in C (AFAIK) since
it requires a real "constant integer expression" and not just a value
known at compile time.
Tim Rentsch
2024-07-13 18:50:48 UTC
Permalink
Post by Alan Mackenzie
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.
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.)
First, I don't know why you think doing this with macros is
impractical. I knocked out a full macro version without too much
difficulty.

Second, if the C you're using has _Static_assert available, the test
can be done using that. (Richard Damon explains how to get a
similar effect to _Static_assert for C versions before C99.)

Here is an illustrating implementation in C11. I changed the types
of the arguments offset and length to be unsigned but otherwise it
is just as you outlined. Oh, the error message has an extra pair of
parentheses to avoid problems with macro processing.


typedef unsigned char u8;
typedef unsigned u32;

static inline u32 (ACM_BITFIELD)( u8 a[], unsigned, unsigned );

#define ACM_BITFIELD( bytes, offset, length ) ( \
(void) ACM_BITFIELD_SANITY_CHECK( offset, length ), \
(ACM_BITFIELD)( (bytes), (offset), (length) ) \
)

#define ACM_BITFIELD_SANITY_CHECK( o, n ) ( \
(struct { \
int x; \
_Static_assert( \
ACM_BITFIELD_TEST_( (o), (n) ), \
STRINGIZE( Invalid arguments (o,n) to ACM_BITFIELD ) \
); \
}) {1} \
)

#define ACM_BITFIELD_TEST_(offset,length) ( \
0 < length && length < 8 && offset%8 +length <= 8 || \
offset%8 == 0 && length%8 == 0 && 0 < length && length <= 32 \
)

#define STRINGIZE(m) #m

u32
(ACM_BITFIELD)( u8 a[], unsigned o, unsigned n ){
unsigned b = o/8;
return
n < 8 ? a[ o/8 ] >> (8 - o%8 - n) & ~(-1u << n) :
n == 32 ? a[b]*16777216u + a[b+1]*65536u + a[b+2]*256u + a[b+3] :
n == 24 ? a[b]*65536u + a[b+1]*256u + a[b+2] :
n == 16 ? a[b]*256u + a[b+1] :
/*****/ a[b];
}
Alan Mackenzie
2024-07-14 15:44:34 UTC
Permalink
Hello, Tim.
Post by Tim Rentsch
Post by Alan Mackenzie
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.
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.)
First, I don't know why you think doing this with macros is
impractical. I knocked out a full macro version without too much
difficulty.
I wanted to generate code conditionally, depending on the macro's
arguments, for efficiency's sake. I don't think this is possible - the C
preprocessor is not Lisp. What I missed was that the compiler's
optimizer will eliminate the superfluous code anyway, so it doesn't
really matter.
Post by Tim Rentsch
Second, if the C you're using has _Static_assert available, the test
can be done using that. (Richard Damon explains how to get a
similar effect to _Static_assert for C versions before C99.)
_Static_assert will only work within a macro. It doesn't work in an
inline function, whose parameters are not constant expressions, despite
being constants known at compile time. I like the way you've got around
this, below.
Post by Tim Rentsch
Here is an illustrating implementation in C11. I changed the types
of the arguments offset and length to be unsigned but otherwise it
is just as you outlined. Oh, the error message has an extra pair of
parentheses to avoid problems with macro processing.
Thanks. I'll probably use something like that after my attempts last
night failed. Just that the byte order needs to be little-endian rather
than big-endian. Having unsigned parameters indeed makes sense, seeing
as how it eliminates trouble with negative lengths and offsets.
Post by Tim Rentsch
typedef unsigned char u8;
typedef unsigned u32;
static inline u32 (ACM_BITFIELD)( u8 a[], unsigned, unsigned );
#define ACM_BITFIELD( bytes, offset, length ) ( \
(void) ACM_BITFIELD_SANITY_CHECK( offset, length ), \
(ACM_BITFIELD)( (bytes), (offset), (length) ) \
)
#define ACM_BITFIELD_SANITY_CHECK( o, n ) ( \
(struct { \
int x; \
_Static_assert( \
ACM_BITFIELD_TEST_( (o), (n) ), \
STRINGIZE( Invalid arguments (o,n) to ACM_BITFIELD ) \
); \
}) {1} \
)
#define ACM_BITFIELD_TEST_(offset,length) ( \
0 < length && length < 8 && offset%8 +length <= 8 || \
offset%8 == 0 && length%8 == 0 && 0 < length && length <= 32 \
)
#define STRINGIZE(m) #m
u32
(ACM_BITFIELD)( u8 a[], unsigned o, unsigned n ){
unsigned b = o/8;
return
/*****/ a[b];
}
--
Alan Mackenzie (Nuremberg, Germany).
Kaz Kylheku
2024-07-14 22:55:56 UTC
Permalink
Post by Alan Mackenzie
I wanted to generate code conditionally, depending on the macro's
arguments, for efficiency's sake. I don't think this is possible - the C
preprocessor is not Lisp. What I missed was that the compiler's
optimizer will eliminate the superfluous code anyway, so it doesn't
really matter.
The C preprocessor is a kind of token-churning functional language.
One way it can make decisions is by token-pasting together the name
of a macro.

$ gcc -E -
#define FOO_OR_BAR(ARG, WHICH) MAC ## WHICH(ARG)
#define MAC0(ARG) FOO(ARG)
#define MAC1(ARG) BAR + ARG

FOO_OR_BAR(42, 0)
FOO_OR_BAR(73, 1)
// [Ctrl-D][Enter] here

# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "<stdin>"


FOO(42)
BAR + 73

A couple of years ago I made a project called "cppwawk" which combined
Awk with the GNU C preprocessor. https://www.kylheku.com/cgit/cppawk/about/

It has a small library of headers including a multi-clause loop macro
that supports parallel and nested iteration, and programmer-definable
clauses!

#include <iter.h>

BEGIN {
loop (from_step (vel, 5, -1),
from_step (pos, 0, vel),
while (pos >= 0),
maximizing (maxpos, pos))
{
print pos
}
print "maxpos =", maxpos
}

Output:

0
4
7
9
10
10
9
7
4
0
maxpos = 10
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Tim Rentsch
2024-07-16 15:31:36 UTC
Permalink
Post by Alan Mackenzie
Hello, Tim.
Post by Tim Rentsch
Post by Alan Mackenzie
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.
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.)
First, I don't know why you think doing this with macros is
impractical. I knocked out a full macro version without too much
difficulty.
I wanted to generate code conditionally, depending on the macro's
arguments, for efficiency's sake. I don't think this is possible - the C
preprocessor is not Lisp. What I missed was that the compiler's
optimizer will eliminate the superfluous code anyway, so it doesn't
really matter.
Right. Looking at code generated from my full macro implementation,
all the overhead (testing to see if the appropriate condition was
met) was optimized away, by both gcc and clang, at level O1. So
that's pretty safe.
Post by Alan Mackenzie
Post by Tim Rentsch
Second, if the C you're using has _Static_assert available, the test
can be done using that. (Richard Damon explains how to get a
similar effect to _Static_assert for C versions before C99.)
_Static_assert will only work within a macro. It doesn't work in an
inline function, whose parameters are not constant expressions, despite
being constants known at compile time. I like the way you've got around
this, below.
Yeah. Using _Static_assert in an expressional context needs a bit
of hoop jumping, because _Static_assert by itself is a statement,
and not an expression. Fortunately a _Static_assert can be embedded
inside a struct definition, which can be used to accomplish the goal.
Post by Alan Mackenzie
Post by Tim Rentsch
Here is an illustrating implementation in C11. I changed the types
of the arguments offset and length to be unsigned but otherwise it
is just as you outlined. Oh, the error message has an extra pair of
parentheses to avoid problems with macro processing.
Thanks. I'll probably use something like that after my attempts last
night failed. Just that the byte order needs to be little-endian rather
than big-endian. Having unsigned parameters indeed makes sense, seeing
as how it eliminates trouble with negative lengths and offsets.
Here is a full macro implementation, including a little-endian byte
order. In addition to being all-macro, I think the code here is a
little cleaner (and using little endian made the byte extraction
simpler and easier).


#define ACM_BITFIELD( bytes, offset, n ) ( \
ACM_IS_SUB_BYTE( offset, n ) ? ACM_BITS( bytes, offset, n ) : \
ACM_IS_FULL_BYTES( offset, n ) ? ACM_BYTES( bytes, offset, n ) : \
ACM_BITFIELD_MESSED_UP( bytes, offset, n ) \
)

#define ACM_IS_SUB_BYTE( at, n ) ( \
0 <= (at) && 0 < (n) && (n) < 8 && (at)%8 + (n) <= 8 \
)

#define ACM_BITS( bytes, at, n ) ( \
(bytes)[ (at)/8 ] >> (8 - (at)%8 - (n)) & ~(-1u << (n)) \
)

#define ACM_IS_FULL_BYTES( at, n ) ( \
0 <= (at) && (at)%8 == 0 && 0 < (n) && (n) < 33 && (n)%8 == 0 \
)

#define ACM_BYTES( bytes, at, n ) ( \
(bytes)[(at)/8] + (((n) > 8 ? (bytes)[(at)/8+1] : 0u) << 8) \
+ (((n) > 16 ? (bytes)[(at)/8+2] : 0u) << 16) \
+ (((n) > 24 ? (bytes)[(at)/8+3] : 0u) << 24) \
)

#define ACM_BITFIELD_MESSED_UP( bytes, at, n ) ( \
(unsigned) sizeof (struct { \
int unused_irrelevant_member; \
_Static_assert( \
ACM_IS_FULL_BYTES( at, n ) || ACM_IS_SUB_BYTE( at, n ), \
STRINGIZE( Invalid arguments (at,n) to ACM_BITFIELD ) \
); \
}) \
)

#define STRINGIZE(x) #x

Loading...