Warning: mysqli::__construct(): (HY000/1203): User howardkn already has more than 'max_user_connections' active connections in D:\Inetpub\vhosts\howardknight.net\al.howardknight.net\includes\artfuncs.php on line 21
Failed to connect to MySQL: (1203) User howardkn already has more than 'max_user_connections' active connections
Warning: mysqli::query(): Couldn't fetch mysqli in D:\Inetpub\vhosts\howardknight.net\al.howardknight.net\index.php on line 66
Article <86plrd5p5z.fsf@linuxsc.com>
Deutsch   English   Français   Italiano  
<86plrd5p5z.fsf@linuxsc.com>

View for Bookmarking (what is this?)
Look up another Usenet article

Path: ...!news.nobody.at!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail
From: Tim Rentsch <tr.17687@z991.linuxsc.com>
Newsgroups: comp.lang.c
Subject: Re: Is it possible to generate a compile time error from an inline function?
Date: Tue, 16 Jul 2024 08:31:36 -0700
Organization: A noiseless patient Spider
Lines: 117
Message-ID: <86plrd5p5z.fsf@linuxsc.com>
References: <v6tu04$klr$1@news.muc.de> <86ikx96s8n.fsf@linuxsc.com> <v70rp2$2723$1@news.muc.de>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Injection-Date: Tue, 16 Jul 2024 17:31:40 +0200 (CEST)
Injection-Info: dont-email.me; posting-host="794e2a49dde28f77182e638dfed6694f";
	logging-data="1405006"; mail-complaints-to="abuse@eternal-september.org";	posting-account="U2FsdGVkX19bZFrXBwbqe4AhhTfUr0Zb20VgXnDYNWU="
User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux)
Cancel-Lock: sha1:eMfcqHmoTdwO4u9aP920/bfPQ+0=
	sha1:DHNbxbt+XypxXVrmP6P3IDfolnc=
Bytes: 6244

Alan Mackenzie <acm@muc.de> writes:

> Hello, Tim.
>
> Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
>
>> Alan Mackenzie <acm@muc.de> writes:
>>
>>> 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.)
>>
>> 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.

>> 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.

>> 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