“调试”中 std::array 的绑定检查海湾合作委员会版本

发布于 2024-12-17 07:43:03 字数 942 浏览 0 评论 0 原文

专家已经解释了 C++11 std::array 在编程时的好处,但我想从编译器那里得到一件事。能够在编译使用 [] 的代码时打开使用 .at() 时默认的范围检查。

这对于检查范围违规可能很有用,特别是对于多维数组,因为在这种情况下,范围违规不太可能导致段错误(因为您经常拥有内部数组周围的内存,因此[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() - 我可能也需要性能我已经编写了很多带有 [] 的代码,但我还不够聪明,无法替换一维数组,更不用说二维数组了。

我使用海湾合作委员会4.6

Benefits of C++11 std::arrays when programming have been explained by experts, but there is one thing I would like to get from the compiler. Ability to turn ON range check that is default when using .at() while compiling the code that uses [].

It could be beneficial for checking range violations especially for multidimensional arrays because in that case it is less likely that range violation will cause segfault(because you often own memory around the inner array so [5000][-123] will still likely point to 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 might need the performance, also I have a lot of code with [] already written and I'm not smart enough to smart to do replace for 1D let alone 2D arrays.

I use GCC 4.6

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

兰花执着 2024-12-24 07:43:03

看起来gcc 4.6自带的数组还没有调试模式。这是可以理解的,因为 C++11 支持仍处于实验阶段。

有一个标志_GLIBCXX_DEBUG通常用于打开调试模式。如果您查看 /usr/include/c++/4.6/debug/vector: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

希望它不会太远,我想我们会有安全的迭代器。调试模式下的数组很快就完成了,这可以是我们的小秘密:-)。

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 :-).

夜夜流光相皎洁 2024-12-24 07:43:03
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) 即可。

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.

呆° 2024-12-24 07:43:03

您可以模拟您想要的行为:

#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 并不完全匹配,但如果这对您很重要,则可以修复。然后将所有对 std::array 的引用替换为 my_array,您将获得用于调试版本的范围检查 operator[]

(我已经使用模板别名来简化 NDEBUG 代码,但我还无法在我的编译器上实际测试它)

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)

皓月长歌 2024-12-24 07:43:03

与其说它是gcc,不如说是gcc 附带的libstdc++ 标准库实现(如果您愿意,您可以自由地使用其他实现)。

libstdc++ 有一个可用于调试 -D_GLIBCXX_DEBUG,但是您应该注意,此调试模式会更改类型的 ABI,因此您需要与也已启用此调试模式编译的库的链接。这可能会很痛苦。

libc++ 是另一个实现(接近 C++11 兼容),它首先针对 Clang,但应该适用于任何兼容的编译器。它的目的是维护 ABI 兼容性,无论是否启用调试。不过,它在 OS X 之外并不完全稳定(主要是由于区域设置),因此可能无法在您的环境中使用。

请注意,这两个库都是免费软件,因此如果未实施检查,您完全可以提交补丁。

It is not so much gcc as libstdc++ the Standard Library implementation that comes with gcc (you are free to use another implementation if you wish).

libstdc++ has a preprocessor flag that can be used for debugging -D_GLIBCXX_DEBUG, however you should note that this debug mode changes the ABI of the types, and thus you need to link with libraries that have also been compiled with this debug mode enabled. It can be painful.

libc++ is another implementation (nigh C++11 compliant) that is first aimed at Clang but should work on any compliant compiler. It aims at maintaining ABI compatibility whether debugging is enabled or not. It's not fully stable outside of OS X though (mostly because of locale) so might not be usable in your environment.

Note that both those libraries are Free Software, so if the check is not implemented, you can perfectly submit a patch.

折戟 2024-12-24 07:43:03

我想要类似的东西,所以写了这个小标题工具
这允许您执行 set(arr, pos, value) 和 get(arr, pos) 以及
可以启用中止、断言或允许其继续
崩溃,没有检查。

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

其要点如下(我在 github 上也有 2 维和 3 维示例)

对于调试版本:

    #define DEBUG
    #define ENABLE_ASSERT
    #define ENABLE_ABORT
    #include "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

I wanted something similar, so wrote this small header only tool
that allows you to do set(arr, pos, value) and get(arr, pos) and
can either enable abort, assert, or allow it to continue on and
crash, with no checking.

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

The gist of it is as follows (I have 2 and 3 dimensional examples too on github)

For debug builds:

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

Some details of the implementation:

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 include a trace of set() and get() calls, enable:

#define DEBUG

To print an assertion on out of bounds (and continue):

#define ENABLE_ASSERT

To call abort() on assertion:

#undef ENABLE_ABORT

hth

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文