Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static_assert on indices know at compile time

Tags:

Is there a way to statically assert on indices known at compile-time and run-time assert otherwise? Example:

template <class T, int Dim>
class Foo
{
    T _data[Dim];
    public:
        const T &operator[](int idx) const
        {
            static_assert(idx < Dim, "out of range"); // error C2131: expression did not evaluate to a constant
            return _data[idx];
        }
};

int main()
{
    Foo<float, 2> foo;

    foo[0];
    foo[1];
    foo[2]; // compiler error

    for (int i=0; i<5; ++i)
    {
        foo[i]; // run time assert when i > 1
    }

    return 0;
}
like image 949
sharvey Avatar asked Feb 12 '17 23:02

sharvey


2 Answers

You could simply throw an exception or assert. It will fail compilation in constexpr context. This only works if the condition for throwing can be evaluated in constexpr context. Note that there is a bug in some versions of gcc that prevent the throw from working.

like image 85
Walther Avatar answered Oct 10 '22 22:10

Walther


I don't think it's possible obtain what do you want with a single function.

Even if you develop a constexpr function, I don't think your are able to detect when is executed run-time and when executed compile time and act in a different way.

But you can develop different functions.

By example, a template get<>(), where the template argument is the index, that can be used only with an index known at compile-time and that can perform a static_assert(), and an at(std::size_t), that can receive an index computed at run time with a run time check.

En passant:

1) I suggest, as usual in STL, the use of at() for a bound checked access and an operator[]() for a bound unchecked access

2) and I suggest the use of an unsigned index or you have to check that the index is >= 0.

The following is a working example

#include <iostream>
#include <stdexcept>

template <class T, std::size_t Dim>
class Foo
 {
   private:
      T _data[Dim];

   public:
      T const & operator[] (std::size_t idx) const
       { return _data[idx]; }

      template <std::size_t IDX>
      T const & get () const
       {
         static_assert(IDX < Dim, "out of range");

         return this->operator[](IDX);
       }

      T const & at (std::size_t idx) const
       {
         if ( idx >= Dim )
            throw std::range_error("out of range");

         return this->operator[](idx);
       }
 };

int main ()
 {
   Foo<float, 2U> foo;

   foo.get<0U>();
   foo.get<1U>();
   //foo.get<2U>(); // compiler error

   for ( auto i = 0U ; i < 5U ; ++i )
      foo.at(i); // run time exception when i > 1

   return 0;
 }
like image 27
max66 Avatar answered Oct 11 '22 00:10

max66