0

I looking for some code that can generate a compile time error when a variable contains a wrong value.

I once started with this code in a c program:

#define FIFO_BUFFER_SIZE 8 
 
#define FIFO_BUFFER_MASK ( FIFO_BUFFER_SIZE - 1 ) 
 
#if ( FIFO_BUFFER_SIZE & FIFO_BUFFER_MASK ) 
#error Fifo buffer size is not a power of 2 
#endif 

Rewrote that in C++ for a PC console application:

MovingAVG::MovingAVG( const unsigned long element_count ) { 
    ptr = 0; 
    siz = element_count; 
    buf = new long[siz]; 
    msk = siz - 1; 
    if( siz & msk ) { 
        //std::cerr << "Buffer size is not a power of 2" << std::endl; 
        //exit( -1 ); 
    } 
} 

Now I want to use something similar in an Arduino sketch. Because there is not always a terminal connected to the serial port and exit() is useless in a microcontroller, I am looking for a way to abort compilation based on a value in a variable. I don't give myself much chance to find anything but to be absolutely sure that there is really no way, I am dropping the question here.

I want to abort compiling (preferably with a message) when the parameter of a class-constructor contains an invalid value. Is that possible at all?

Actually I want to see a message before I start programming the device. I have posted a possible solution below, that will throw a message during linking, but that only works when I declare the class with a constant parameter:

class MovingAVG avg( 32 );
2
  • 6
    I'm not sure what to do with this, because it's probably better asked and answered on stackoverflow. It almost certainly has been. You are sort of looking for static_assert. However, to make use of that you have actually have a constant expression (compile-time knowable calculation) and siz & msk isn't knowable at compile-time. So the better part trying to answer this would seem to be about transforming your rewrite into something that could be evaluated at compile time which is something you'd probably have make decisions about that we can't.
    – timemage
    Commented Jan 2, 2023 at 14:05
  • 1
    The second code throws the error at execution time, not at compilation time, thus not want to asked for. The first code snippet would do that. What exactly is the reason that you don't want to use it?
    – chrisl
    Commented Jan 2, 2023 at 17:21

3 Answers 3

3

As suggested by timemage, I think static_assert() is the right solution to your problem: it is the clearest and most semantically correct. The problem is, you cannot plug static_assert() directly into your code, as the expression you are testing is not a compile-time constant.

I suggest you turn the argument of the constructor into a template argument. This will guarantee it is a compile-time constant. As a bonus, you will be able to use a statically-allocated buffer, which is nicer on the RAM than dynamic allocation:

template <int siz> class MovingAVG {
    static const int msk = siz - 1;
    long buf[siz];
    int ptr = 0;
public:
    MovingAVG()
    {
        static_assert((siz & msk) == 0,
                "Element count should be a power of two.");
    }
};

This works:

MovingAVG<32> filter;

but this fails with “error: static assertion failed: Element count should be a power of two.”:

MovingAVG<33> filter;
2
  • Yeah. If this is a design decision they can take, it's what I'd go with. Where we're not getting them to clarify, I'm voting this up.
    – timemage
    Commented Jan 3, 2023 at 19:49
  • Excellent coding. I like it! Thank you.
    – hennep
    Commented Jan 3, 2023 at 20:24
1

This will do the trick, a very dirty trick but it will generate an error before I program/test the device. That might save a few program cycles of the device, but more important it will not waste my time.

extern void ElementCountShouldBeAPowerOfTwo(void);

MovingAVG::MovingAVG( const unsigned long element_count ) {
    ptr = 0;
    siz = element_count;
    buf = new long[siz];
    msk = siz - 1;
    if( siz & msk ) {
        // This function will be removed by the optimiser 
        // when the buffer size is a power of 2 
        ElementCountShouldBeAPowerOfTwo(); 
    }
}

The compiler will not complain about the function "ElementCountShouldBeAPowerOfTwo" because it is marked as external. The linker will throw an error when the optimizer leaves the call in place.

In case the linker throws an error it will show:

undefined reference to `ElementCountShouldBeAPowerOfTwo()'

@chrisl I've used both code in C and in C++ and I don't want to rewrite the C++ part. You and @the_busybee were both right when you wrote that the error will not be shown at compile time.

I should have asked my question differently, I want to see a warning before I program the device :-)

I do not mark an answer because I expect this to fail in the future when the compiler is updated. For the moment, with Arduino 1.8.19, it does what I want.

1
  • It does not matter how you turn it, like any other method, this can work only if the value is known at compile time. And this is true only if the compiler sees all calls (in this case of the constructor) and their arguments are constant. So the usability is really very small. It would be good to add this to your question. Commented Jan 3, 2023 at 12:59
1

Because element_count is a method's parameter, it is by definition variable. Therefore, its value is generally not known at compile time.

Since the value of element_count is not known at compile time, you cannot get a compile time error for a wrong value.


Only if you use the method (here: a constructor) in the same translation unit as its definition, and if the arguments of all calls are known to the compiler (they are constants), the compiler will be able to evaluate siz & msk as a constant. Please note that some compilers need to receive optimization options for this.

Then you can use any approach that triggers a compiler diagnostic, for example like this one in C.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.