Bound checking of std::array in "Debug" version of GCC
It looks like the array that comes with gcc 4.6 doesn't have a debug mode yet. Understandable since C++11 support is still experimental.
There is a flag _GLIBCXX_DEBUG
which is usually used to turn on debug mode. If you look at /usr/include/c++/4.6/debug/vector:313 you'll see operator[]
has:
__glibcxx_check_subscript(__n);
Now, this may be uber-evil (and I mean really evil) but it looks like we can conditionally add this to array. Change lines 148-154 of /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]; }
to:
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 you can enable bounds checking for array the same way you do for vector and other stl debugging - by adding -D_GLIBCXX_DEBUG
to your compile line. E.g.:
g++ someAwesomeProgram.cpp -D_GLIBCXX_DEBUG
I just had a look at gcc trunk and apparently there is no reference to _GLIBCXX_DEBUG for array yet :(. http://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/include/std/array
Hopefully it's not too far away. I imagine we will have safe iterators and all that for array in debug mode soon enough. In the meantime, this can be our little secret :-).
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));
}
Should do the job. Just use at(arr, pos)
consistently throughout the codebase.
You can emulate the behaviour you desire:
#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
It doesn't perfectly match std::array
, but that could be fixed if it matters to you. Then replace all reference to std::array
with my_array
and you'll get range checked operator[]
for debug builds.
(I'd have used template aliases to simplify the NDEBUG
code, but I can't actually test that yet on my compiler)