1

I need a macro variable check at design time (preprocesor), more specific that number to fit in 24 bits. The macro is intended to be used in a if() statement so I have no idea how to test it.

This is a ARM systick timer (24 bits) and so many time I forgot to #define the right value, especially when change the MCU clock and of course, my if() never fired and this silly mistake was hard to debug.

So in this example, there is a trick to force gcc to ERROR when PARAMETER > 24 bits ?

    #define PARAMETER   20000000  // over 24 bits, should throw a error at design time
    #define MyMacro(var, par)       (var > par)

    uint32_t variable;

    if(MyMacro(variable,PARAMETER))
    {
        // do something
        //  do something WRONG because PARAMETER > 24 bits


        // Actually this is working as expected, test for < is valid because 
// _Static_assert() is check for TRUE condition
// But I am still trying to find a way to combine this in original macro
        _Static_assert(PARAMETER < 0xFFFFFF, "Ooopss... ERROR");

    }

Thanks in advance!

yo3hcv
  • 1,531
  • 2
  • 17
  • 27

3 Answers3

1

Unfortunately, _Static_assert is syntactically defined as a declaration, which means you can't use it directly inside of an expression.

However, _Static_assert isn't needed anyway, because you can perfectly (sans the nice compile time error reporting--but you're a programmer, you should be able to figure out a compile time failure a slightly more technical compile-time error message) emulate it with

#define static_assert_0expr(Truth) ((int)(0*sizeof(struct { int _ : (Truth)?1:-1; })))

(or an equivalent) and that you can fit in an expression (even an integer constant expression) no problem:

#define static_assert_0expr(Truth) ((int)(0*sizeof(struct { int _ : (Truth)?1:-1; })))

#define PARAMETER   20000000  // over 24 bits, should throw a error at design time
#define MyMacro(var, par)       (static_assert_0expr((par)<0xffffff) + ((var) > (par)))

//or this, but this is won't preserve integer-constant expressions because of the comma:
/*#define MyMacro(var, par)       (static_assert_0expr((par)<0xffffff), ((var) > (par)))*/
//alternatively: (static_assert_0expr(assertion) ? (expr) : (expr)) is the most
//general form (though it leads to larger preprocessor expansions, which may worsen debugging experience with cc -E)

#include <stdint.h>
int main()
{
    static_assert_0expr(1)+1;
    uint32_t variable;

    if(MyMacro(variable,PARAMETER))
    {
    }
}

The above static_assert_0expr macro could also be implemented with _Static_assert:

#define static_assert_0expr(Truth) \
   ((int)(0*sizeof(struct { int _; _Static_assert(Truth,""); })))

or you could paste the body of this directly in MyMacro and customize the message (but I consider _Static_assert and its custom compile-time error message feature an unnecessary addition to C and prefer not to use it).

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
1

Well, I don't want to reply my own answer, but I think I found a solution that is working (thanks @PSkoicik) and thanks to GCC that allows statement expressions (found in this reply) Using and returning output in C macro

So basically I could use _Static_assert() inside if() statement, with a helper macro

#define CheckParameter(val) ({bool retval = true; _Static_assert((val)< 0xFFFFFF, "Timer value too large!"); retval;})

Now my macro become

#define MyMacro(var, par)       ((var > par) && CheckParameter(par))

Which should work because CheckParameter() will always return TRUE at RUNTIME but at COMPILE time, _Static_assert() will catch my error parameter.

So now I can use

if(MyMacro(variable,PARAMETER))
{
// PAREMETER will be in range
}

Hope I'm not missing something :)

yo3hcv
  • 1,531
  • 2
  • 17
  • 27
  • Expressions statements (i.e., putting () around a compound statement (block)) are a common extension, but they're not technically standard C. They're also not usable at filescope (unless gcc devs grant me my wish: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93239) and inside integer constant expressions. – Petr Skocik May 15 '20 at 08:05
0

If you need to check that PARAMETER is > 24 bits during compile time you can simply do this:

#define PARAMETER   20000  // over 24 bits, should throw a error at design time
...
#if PARAMETER > (1<<24)
#error PARAMETER > 24 bits
#endif

What you do here is not compile time checking but run time checking:

if(MyMacro(variable,PARAMETER))
{
    // do something
    //  do something WRONG because PARAMETER > 24 bits
}

but what is variable doing here anyway if you just want to know if PARAMETER is > 24 bits?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • indeed, but I have a LOT of timers in application. will require to add this check for each one, isn't it? I am trying to check in (with) my macro itself – yo3hcv May 15 '20 at 06:50
  • @orfruit no, you check it once in some header file. And please explain what `variable` is doing here?? – Jabberwocky May 15 '20 at 06:51
  • @orfruit or just tell us what exactly `MyMacro(variable,PARAMETER)` is supposed to do. Make that clear in your question, [edit your question](https://stackoverflow.com/posts/61813086/edit) – Jabberwocky May 15 '20 at 06:52
  • Please see edited with _Static_assert(), the macro is just a comparision test that's all – yo3hcv May 15 '20 at 06:53