1

I search for a method to detect a given value for a constexpr constructor is in a valid range.

The code is as follows:

class Timer0 {

constexpr uint16_t CalcPresecaler( uint32_t faktor )
{   
    uint16_t prescaler = faktor / 255;

    if ( prescaler <    1 ) return    1;  
    if ( prescaler <    8 ) return    8;  
    if ( prescaler <   64 ) return   64; 
    if ( prescaler <  256 ) return  256;
    return 1024;
}   

constexpr uint8_t PrescalerBits( uint16_t prescaler )
{   
    if ( prescaler == 1024 ) return 0b101;
    if ( prescaler ==  256 ) return 0b100;
    if ( prescaler ==   64 ) return 0b011;
    if ( prescaler ==    8 ) return 0b010;
    if ( prescaler ==    1 ) return 0b001;
    return 0b000; // should never happen
}  
    constexpr Timer0( const uint32_t sysclk, const uint32_t timerclk )
    {
        uint32_t faktor = sysclk/timerclk; 
        uint16_t prescaler = CalcPresecaler( faktor );
        uint8_t prescalerBits = PrescalerBits( prescaler );
        uint8_t compare = (faktor/prescaler)-1;
        static_assert( ???? ); << can not be used on function parameters
        OCR0=compare;
        TCCR0=0x08| prescalerBits;
        TIMSK=0x02;
    }
};
int main()
{
     Timer0 t0(  8000000, 10 );
}

I already read C++11 - static_assert within constexpr function? but I can't use throw as I am on a avr embedded target where gcc has not enabled exceptions, even if it used here for compile time checking.

Any idea how to detect at compile time that the value of compare did not exceed a given value?

Klaus
  • 24,205
  • 7
  • 58
  • 113

2 Answers2

0

Since you cannot throw, you can do something that is illegal in a constexpr environment.

For example, if your condition is that prescalerBits != 0, you can do:

if (prescalerBits == 0)
    *(int*)nullptr = 0;

Which should stop any compile time evaluation. If you are worried about calling this function at runtime and getting a null pointer dereference, you can guard this condition with std::is_constant_evaluated().

user975989
  • 2,578
  • 1
  • 20
  • 38
  • any chance to detect anf get a compile error in the case that we are not constant evaluated? Can't use it in the context of constexpr if nor in static_assert. So if a lib user calls for non constexpr context, weget no error at all, even if we did not get the nullptr access. – Klaus Feb 05 '21 at 13:01
0

Your code is not really self-contained so it is not clear exactly what the problem is, so we will solve similar but simpler problem:

Construct a rectangle at compile time and check it is not a square (lengths of sides must be different).

First one uncomfortable C++ truth:

There are no constexpr function arguments in C++.

It is confusing to many developers, including me, but it makes sense when you understand what it would mean, but that is a different long story...

Hack/workaround this it to use template arguments as a way to have "constexpr function arguments".

Simple example:

constexpr void fn(const int val){
  // does not compile even when val is 47: fn(47).
  //static assert(val==47);
}

template<auto val>
constexpr void fn_cx(){
  // does  compile  when called with 47:  fn<47>()>
  static_assert(val==47);
}

now to implement our Rectangle example:

class Rectangle{
    public:
    constexpr Rectangle(const int a, const int b): a_(a), b_(b){} 
    template<auto/*you can also use int here*/ a, auto b>
    static constexpr Rectangle MakeCx(){
        static_assert(a!=b,"square not allowed");
        return Rectangle(a,b);
    }
    constexpr int area() const{
        return a_*b_;
    }
    private:
    int a_;
    int b_;
};


int main()
{
    // ok
    constexpr auto rect = Rectangle::MakeCx<2,3>();
    // static_assert fails
    // constexpr auto square = Rectangle::MakeCx<2,2>();
    return rect.area();
}

Note: you mention you are using some predefined registers, those obviously can not be written to in constexpr constructor.

NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277