Home > Software design >  Throw compiler error if const value is wrong (static assert)
Throw compiler error if const value is wrong (static assert)

Time:09-27

I made a class for an arduino program. Inside the class I would like to toss a compiler error if a wrong pin number is passed as an argument.

class AnalogOutput : public AnalogBlock
{
public:

    AnalogOutput( uint8_t _pin ) : pin( _pin )
    {        
        static_assert
        (       pin ==  3 
            ||  pin ==  5
            ||  pin ==  6
            ||  pin ==  9
            ||  pin == 10
            ||  pin == 11 , "INVALID PWM PIN USED" 
        ) ;
    }

    void run()
    {
        if( IN2 != prevIn )
        {   prevIn  = IN2 ;                // if incomming change, update PWM level

            analogWrite( pin, IN2) ;
        }
    }
  
private:
    const uint8_t pin ;
    uint8_t       prevIn ;
} ;

The constructor is only called with compile-time constants.

static ServoMotor M1 = ServoMotor( 3 ) ; // 3 is the pin number

Yet I get me this compiler error

error: non-constant condition for static assertion
         static_assert (
         ^~~~~~~~~~~~~
error: use of 'this' in a constant expression

I looked here but it did not make me wizer. It is the first time that I am trying to use static_assert().

First question: what I am trying to do, can that be done in the first place? Second question: providing that the previous answer is 'yes' how can it be done?

CodePudding user response:

static_assert is for compile-time constants only, thus, because you try to run something in the context of object construction (hence, runtime), the compiler refuses to generate code.

An alternative solution would be to use a templated class like such:

template<uint8_t pin>
class AnalogOutput
{
        static_assert
        (       pin ==  3 
            ||  pin ==  5
            ||  pin ==  6
            ||  pin ==  9
            ||  pin == 10
            ||  pin == 11 , "INVALID PWM PIN USED" 
        ) ;
    //blablabla
};

Though I remember having bad times with template in Arduino due to bugs in their compiler. It was a few years ago, but still, use at your own risks.

CodePudding user response:

Instead of taking an uint8_t, take an auto and pass an std::integral_constant<uint8_t, value>(), where value is the value you'd like to pass (e.g. 3). It can be checked compile-time and can be converted to the underlying integral type, i.e., uint8_t.

CodePudding user response:

A way without using any templates, is to make a consteval function that only returns the constructed object if the condition is met. If the condition was false, it returns nothing and therefore the compiler will throw an error:

#include <cstdint>

struct ServoMotor {
    constexpr ServoMotor(uint8_t pin) {}
};

constexpr bool valid_pin(uint8_t pin) {
    return (pin == 3 || pin == 5 || pin == 6 ||
            pin == 9 || pin == 10 || pin == 11);
}
consteval auto make_motor(uint8_t pin) {
    if (valid_pin(pin)) {
        return ServoMotor(pin);
    }
}

int main() {
    auto m3 = make_motor(3);
    auto m4 = make_motor(4);
}

the line on m3 works as expected, but there is an error C7595 on the m4 line, since 4 is not a valid pin.


Try it out here.

  • Related