Bound std :: array check in GCC Debug version

Advantages of C ++ 11 std::array when programming was explained by experts, but there is one thing I would like to get from the compiler. The ability to check the default range when using .at() when compiling code using [] .

This can be useful for checking range violations, especially for multidimensional arrays, because in this case it is less likely that range violation will cause segfault (because you often own the memory around the internal array, so [5000][-123] will still be point to the memory that you own).

So, I would like to know if there is a switch that will compile into machine code that checks ranges:

  const uint32_t dim1=10*1000,dim2=3; std::array<std::array<int, dim2>, dim1> test_2Darray; int undefined_value=test_2Darray[dim2-1][dim1-1]; std::cout<<"ouch ("<<undefined_value<<")"<<std::endl; int ok_value=test_2Darray[dim1-1][dim2-1]; std::cout<<"OK ("<<ok_value<<")"<<std::endl; // test_2Darray.at(dim2-1).at(dim1-1); -->terminate called after throwing an instance of 'std::out_of_range' // what(): array::at 

If you ask why I don’t switch to .at() , I may need performance, I also have a lot of code with [] already written, and I'm not smart enough to make smart replacements for 1D, not to mention 2D arrays .

I am using GCC 4.6

+8
source share
5 answers

You can imitate the desired behavior:

 #include <array> #include <cassert> #include <iostream> #ifndef NDEBUG template <typename T, std::size_t N> struct my_array : std::array<T,N> { T& operator[](std::size_t n) { assert(n < N); return (*static_cast<std::array<T,N>*>(this))[n]; } const T& operator[](std::size_t n) const { assert(n < N); return (*static_cast<const std::array<T,N>*>(this))[n]; } }; #else // I would use Alias templates here, but isn't supported on my compiler yet! template <typename T, std::size_t N> struct my_array : std::array<T,N> { }; #endif 

This does not match std::array perfectly, but it can be fixed if it matters to you. Then replace all references to std::array with my_array and you will get a check flag operator[] for debugging collections.

(I would use template aliases to simplify the NDEBUG code, but I cannot verify this yet on my compiler)

+2
source

It looks like the array shipped with gcc 4.6 does not yet have debug mode. It's clear that C ++ 11 support is still experimental.

There is a flag _GLIBCXX_DEBUG , which is usually used to enable debugging mode. If you look at / usr / include / c ++ / 4.6 / debug / vector: 313, you will see that operator[] has:

 __glibcxx_check_subscript(__n); 

Now this can be a terrible evil (and I mean really evil), but it looks like we can conditionally add this to an array. Change lines 148-154 from / usr / include / c ++ / 4.6 / array from:

 reference operator[](size_type __n) { return _M_instance[__n]; } const_reference operator[](size_type __n) const { return _M_instance[__n]; } 

in

 reference operator[](size_type __n) { #ifdef _GLIBCXX_DEBUG __glibcxx_check_subscript(__n); #endif return _M_instance[__n]; } const_reference operator[](size_type __n) const { #ifdef _GLIBCXX_DEBUG __glibcxx_check_subscript(__n); #endif return _M_instance[__n]; } 

This means that you can enable border checking for an array in the same way as for vector and other stl debugging - by adding -D_GLIBCXX_DEBUG to your compilation line. For example:.

 g++ someAwesomeProgram.cpp -D_GLIBCXX_DEBUG 

I just looked at the gcc trunk and there is apparently no link to _GLIBCXX_DEBUG for the array :( http://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/include/std/array

Hope this is not too far. I assume that soon you will have safe iterators and everything for the array in debug mode. In the meantime, this may be our little secret :-).

+5
source
 template<class T, std::size_t N> T const& at(std::array<T,N> const& arr, std::size_t pos){ #ifndef NDEBUG // debug versions, automatically range checked return arr.at(pos); #else // release version, unchecked return arr[pos]; #endif } template<class T, std::size_t N> T& at(std::array<T,N>& arr, std::size_t pos){ typedef std::array<T,N> const& const_array; // const_cast of the return is safe here because be pass a non-const array // const_cast for the argument is needed to avoid infinite recursion return const_cast<T&>(at(const_cast<const_array>(arr), pos)); } 

Must do the job. Just use at(arr, pos) sequentially throughout the code base.

+3
source

Not as much gcc as libstdc++ implementation of the standard library that comes with gcc (you can use a different implementation if you want).

libstdc++ has a preprocessor flag that you can use for debugging - D_GLIBCXX_DEBUG , however you should notice that this debug mode changes the ABI types, and therefore you need to link to libraries that have also been compiled with debug mode turned on. It can be painful.

libc++ is another implementation (almost compatible with C ++ 11) that first targets Clang but should work on any compatible compiler. It aims to maintain ABI compatibility whether debugging is enabled or not. It is not completely stable outside OS X, although (mainly due to the locale), therefore, it may not be used in your environment.

Please note that both of these libraries are free software, so if the check is not implemented, you can completely send the patch.

+2
source

I wanted something similar, so I wrote this little tool just a heading that allows you to set (arr, pos, value) and get (arr, pos) and can enable or disable, confirm or allow continue and fail without checking.

https://github.com/goblinhack/c-plus-plus-array-bounds-checker

The essence of this is as follows (I have 2 and 3-dimensional examples on GitHub)

For debug builds:

  #define DEBUG
     #define ENABLE_ASSERT
     #define ENABLE_ABORT
     #include "array_bounds_check.h"

Some implementation details:

 template<class TYPE, std::size_t XDIM> void set(std::array<TYPE,XDIM>& arr, std::size_t X, TYPE v){ DODEBUG(std::cerr << "set [" << X << "] = " << v << std::endl); ASSERT(X >= 0) ASSERT(X < arr.size()) arr[X] = v; } template<class TYPE, std::size_t XDIM> TYPE& get(std::array<TYPE,XDIM> & arr, std::size_t X){ DODEBUG(std::cerr << "get [" << X << "] = "); ASSERT(X >= 0) ASSERT(X < arr.size()) DODEBUG(std::cerr << arr[X] << std::endl); return (arr[X]); } 

If you want to enable tracing of the calls to set () and get (), enable:

 #define DEBUG 

To print the statement out of bounds (and continue):

 #define ENABLE_ASSERT 

To call abort () to confirm:

 #undef ENABLE_ABORT 

NTN

0
source

All Articles