"Debug"版本的 GCC 中 std::array 的绑定检查
Bound checking of std::array in "Debug" version of GCC
专家已经解释了C++11 std::array
在编程时的好处,但有一件事我想从编译器那里得到。能够在编译使用[]
的代码时,打开使用.at()
时默认的范围检查。
这可能有利于检查范围冲突,尤其是对于多维数组,因为在这种情况下,范围冲突不太可能导致segfault(因为您经常在内部数组周围拥有内存,所以[5000][-123]
仍然可能指向您拥有的内存(。
所以我想知道是否有一个开关可以编译成检查范围的机器代码:
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
如果你问我为什么不切换到.at()
——我可能需要性能,而且我已经写了很多[]
的代码,我还不够聪明,无法智能地替换1D,更不用说2D阵列了。
我使用GCC 4.6
看起来gcc 4.6附带的数组还没有调试模式。这是可以理解的,因为C++11的支持仍然是实验性的。
有一个标志_GLIBCXX_DEBUG
,它通常用于打开调试模式。如果您查看/usr/include/c++/4.6/debug/vvector:313,您将看到operator[]
具有:
__glibcxx_check_subscript(__n);
现在,这可能是超级邪恶(我的意思是真的邪恶(,但看起来我们可以有条件地将其添加到数组中。将/usr/include/c++/4.6/array的第148-154行从:更改为
reference
operator[](size_type __n)
{ return _M_instance[__n]; }
const_reference
operator[](size_type __n) const
{ return _M_instance[__n]; }
至:
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];
}
这意味着您可以像对向量和其他stl调试一样,通过在编译行中添加-D_GLIBCXX_DEBUG
来启用数组的边界检查。例如:
g++ someAwesomeProgram.cpp -D_GLIBCXX_DEBUG
我刚刚看了一眼gcc-trunk,显然还没有引用数组的_GLIBCXX_DEBUG:(。http://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/include/std/array
希望不会太远。我想我们很快就会在调试模式下为数组提供安全的迭代器和所有这些。与此同时,这可能是我们的小秘密:-(。
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));
}
应该做这份工作。只需在整个代码库中一致地使用at(arr, pos)
即可。
您可以模仿您想要的行为:
#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
它并不完全匹配std::array
,但如果它对您很重要,可以修复。然后用my_array
替换对std::array
的所有引用,您将得到调试构建的范围检查operator[]
。
(我本来会使用模板别名来简化NDEBUG
代码,但实际上我还不能在编译器上测试它(
与其说是gcc,不如说是libstdc++
——gcc附带的标准库实现(如果愿意,您可以自由使用另一个实现(。
libstdc++
有一个可用于调试的预处理器标志——D_GLIBCXX_DEBUG,但您应该注意,此调试模式会更改类型的ABI,因此您需要链接到也已在启用此调试模式的情况下编译的库。这可能很痛苦。
libc++
是另一个实现(几乎与C++11兼容(,它首先针对Clang,但应该在任何兼容的编译器上工作。它旨在维护ABI兼容性,无论是否启用调试。不过,它在OSX之外并不完全稳定(主要是因为区域设置(,因此可能无法在您的环境中使用。
请注意,这两个库都是免费软件,所以如果没有实现检查,您可以完美地提交补丁。
我想要类似的东西,所以写了这个只有小标题的工具它允许您执行set(arr,pos,value(和get(arr、pos(以及可以启用中止、断言或允许它在和上继续崩溃,没有检查。
https://github.com/goblinhack/c-plus-plus-array-bounds-checker
它的要点如下(我在github上也有二维和三维的例子(
对于调试版本:
#定义调试#定义ENABLE_ASSERT#定义ENABLE_ABORT#包括"array_bounds_check.h">
实施的一些细节:
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]);
}
如果您想包含set((和get((调用的跟踪,请启用:
#define DEBUG
要在越界上打印断言(并继续(:
#define ENABLE_ASSERT
在断言时调用abort((:
#undef ENABLE_ABORT
hth
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 在基于范围的for循环中使用结构化绑定声明
- 使用 LuaBridge 将 LuaJIT 绑定到C++会导致"PANIC: unprotected error"
- 尝试通过OCI例程从Oracle获取blob数据,但出现错误:ORA-01008:并非所有变量都绑定
- 在使用GPU支持编译Tensorflow时,会遇到CUDA_TOOLKIT_PATH未绑定变量
- 视觉studo 2019中的漫画和静态/动态绑定
- 将自由函数绑定为类成员函数
- 将常量指针引用绑定到非常量指针
- 在派生类中绑定非静态模板化成员函数
- 绑定派生类方法C++从实例范围之外的分隔 std::function 变量调用
- 在 openGL 中多次绑定缓冲区
- 定义有趣的宏和正则表达式在Z3 C++绑定
- 使用结构化绑定'Reflection'
- 为什么 std::绑定错误参数可以成功?
- 如何绑定 C++ gRPC 客户端的网络接口
- 在 openmp 中,omp_get_thread_num是否绑定到物理线程?
- C++绑定(已弃用)
- 运行时错误:引用绑定到类型为"int"的空指针
- 有没有办法将重载的类函数绑定到函数对象?
- "Debug"版本的 GCC 中 std::array 的绑定检查