如何使C++ Package 器为std::vector在终端上超出范围的消息提供文件和行信息并中止

lsmd5eda  于 2023-02-10  发布在  其他
关注(0)|答案(2)|浏览(95)

如何使C++ Package 器为std::vector在终端上的超出范围消息提供文件和行信息并中止?
我写了附加的代码,它完成了这项工作,但需要额外的模板R,它是用来输出的结果,让它编译。
两个问题:
1.有什么方法可以避免使用宏来获取文件和行信息,这样就可以输出了?
1.有没有办法改变它,这样我就可以改变,使函数返回的项目?即std::string s = vec_get(v,idx);
这是当前输出,比stl_vector. h_M_range_check输出(这是STL库)更清晰

$ ./vec_throw2
vec_throw2.cpp:69 err std::vector 0 out of range size 0
terminate called after throwing an instance of 'std::out_of_range'
what():  vec_get
Aborted (core dumped)


The code follows

// g++ -Wall -O2 -o vec_throw vec_throw2.cpp
#include <vector>
#include <cstdio>
#include <stdexcept>

using namespace std;

template<class T, class R>
void vec_get(T v, R r, size_t idx, const char * const file, const int line)
{
    if (idx<v.size())
    {
        r = v.at(idx);
    }
    else
    {
        printf("%s:%d err std::vector %zu out of range size %zu\n", file, line, idx, v.size());
        throw std::out_of_range ("vec_get");
    }
}

#define VEC_GET2(v, w, idx) vec_get(v, w, idx, __FILE__, __LINE__)

int main()
{
    std::vector<std::string> v;

    int ret = 0;
    size_t idx = 0;

    std::string s;
    VEC_GET2(v, s, idx);
    return ret;
}

我尝试了一些不同的方法,包括“自动”返回。

dgtucam1

dgtucam11#

使用std::source_location并只检查一次边界:

#include <iostream>
#include <source_location>
#include <stdexcept>
#include <vector>

template <class T, class R>
void vec_get(T v, R& r, size_t idx, // take r by reference
             const std::source_location loc = std::source_location::current()) {
    try {
        r = v.at(idx); // only one bounds check
    } catch (...) {
        // If you use `printf` you may need to `std::fflush(stdout)` to see your
        // message. Use `fprintf(stderr, ...` or `std::cerr` instead.
        std::cerr << loc.file_name() << ':' << loc.line() << " err std::vector "
                  << idx << " out of range size " << v.size() << '\n';
        throw;  // rethrow the original exception
    }
}

int main() {
    std::vector<std::string> v;
    size_t idx = 0;
    std::string s;

    vec_get(v, s, idx);

    std::cout << s << '\n';
}

可能的输出:

example.cpp:23 err std::vector 0 out of range size 0
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 0) >= this->size() (which is 0)
kfgdxczn

kfgdxczn2#

在c++20中,你可以使用std::source_location。虽然afaik只支持gcc。这样你就不需要宏了。
其实我不知道你在返回元素的时候遇到了什么问题,你可以这样做;)

#include <vector>
#include <iostream>
#include <stdexcept>
#include <source_location>
        
using namespace std;

template<class V>
auto vec_get(V v, size_t idx, std::source_location loc = std::source_location::current()) {
    if (idx >= v.size()) {
        std::cout << loc.file_name() << " " 
                  << loc.line() << " "
                  << idx << " " 
                  << v.size() << std::endl;
        throw std::out_of_range ("vec_get");
    }
    return v[idx];
}

int main() {
    std::vector<std::string> v;

    int ret = 0;
    size_t idx = 0;

    std::string s = vec_get(v, idx);
    return ret;
}

Live Demo
正如在注解中提到的,一旦你检查了索引是有效的并且抛出了你自己的异常,那么调用at就没有意义了。另外,如果你还想在程序终止时看到输出,你必须确保在抛出异常之前刷新流。
然而,我允许自己质疑你的方法,而不是习惯于尝试通过无效的索引访问元素,在实际访问元素之前,你应该确保索引是有效的。2你不能做到这一点的情况非常罕见。3在我的脑海中,我能想象的唯一的例子是当索引来自外部输入时。在这种情况下,您将对输入执行一些健全性检查,而不是依赖容器抛出一些out_of_range错误,这些错误对根本原因一无所知。
另一方面,在很多情况下,你不需要边界检查,例如,在一个迭代元素的循环中,这将是一种浪费:

for (int i=0; i < v.size(); ++i) vec_get(v,i); // please no!

相关问题