• Is it possible to generate a compile time error from an inline function

    From Alan Mackenzie@21:1/5 to All on Sat Jul 13 13:04:04 2024
    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).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Alan Mackenzie on Sat Jul 13 17:15:06 2024
    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.


    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Mackenzie@21:1/5 to David Brown on Sat Jul 13 17:05:22 2024
    Hello, David.

    Many thanks for the reply! It's just what I was looking for.

    David Brown <david.brown@hesbynett.no> 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. :-)

    --
    Alan Mackenzie (Nuremberg, Germany).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Mackenzie@21:1/5 to Richard Damon on Sat Jul 13 17:56:15 2024
    Hello, Richard.

    Richard Damon <richard@damon-family.org> 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 <david.brown@hesbynett.no> 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.

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to Alan Mackenzie on Sat Jul 13 13:34:35 2024
    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 <david.brown@hesbynett.no> 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.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to Alan Mackenzie on Sat Jul 13 14:07:58 2024
    On 7/13/24 1:56 PM, Alan Mackenzie wrote:
    Hello, Richard.

    Richard Damon <richard@damon-family.org> 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 <david.brown@hesbynett.no> 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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Alan Mackenzie on Sat Jul 13 11:50:48 2024
    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.

    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];
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kenny McCormack@21:1/5 to richard@damon-family.org on Sun Jul 14 06:30:29 2024
    In article <e4c6a99e1aa3a28e6f8f6176a6cc6678954156a2@i2pn2.org>,
    Richard Damon <richard@damon-family.org> wrote:
    ...
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Richard Damon on Sun Jul 14 15:05:16 2024
    On 13/07/2024 19:34, 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 <david.brown@hesbynett.no> 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.

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


    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Richard Damon on Sun Jul 14 15:17:35 2024
    On 13/07/2024 20:07, Richard Damon wrote:
    On 7/13/24 1:56 PM, Alan Mackenzie wrote:
    Hello, Richard.

    Richard Damon <richard@damon-family.org> 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 <david.brown@hesbynett.no> wrote:
    On 13/07/2024 15:04, Alan Mackenzie wrote:
    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.



    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Mackenzie@21:1/5 to Tim Rentsch on Sun Jul 14 15:44:34 2024
    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.

    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.

    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.

    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 (Nuremberg, Germany).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Sun Jul 14 22:50:38 2024
    On 14/07/2024 21:14, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 13/07/2024 20:07, Richard Damon wrote:
    [...]
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Alan Mackenzie on Sun Jul 14 22:55:56 2024
    On 2024-07-14, Alan Mackenzie <acm@muc.de> wrote:
    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: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Alan Mackenzie on Tue Jul 16 08:31:36 2024
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)