How to make a C++ wrapper to provide file and line information for a std::vector out of range message on terminal and abort

huangapple go评论49阅读模式
英文:

How to make a C++ wrapper to provide file and line information for a std::vector out of range message on terminal and abort

问题

如何创建一个C++包装器,以便在终端上提供有关std::vector越界消息的文件和行信息,并中止操作?

我编写了附带的代码,它可以完成这个任务,但需要额外的模板R,用于输出结果以使其编译通过。

两个问题:

1)有没有办法避免使用宏来传递文件和行信息,以便可以输出它?

2)有没有办法更改它,使函数返回该项?例如,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)

以下是代码:

// 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;
}

我尝试了一些不同的方法,包括使用'auto'返回。

英文:

How to make a C++ wrapper to provide file and line information for a std::vector out of range message on terminal and abort?

I wrote the attached code, which does the job, but needed the extra template R, which is used to output the result that to get it to compile.

Two questions :-

  1. Is there any way avoid using a MACRO to get the file and line info passed so it can be output?

  2. Is there a way to change it so I could change so the function returns the item? ie std::string s = vec_get(v, idx);

This is the current output, which is clearer than just the stl_vector.h _M_range_check output (this is the STL library)

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



The code follows

// g++ -Wall -O2 -o vec_throw vec_throw2.cpp
#include &lt;vector&gt;
#include &lt;cstdio&gt;
#include &lt;stdexcept&gt;

using namespace std;

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

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

int main()
{
    std::vector&lt;std::string&gt; v;

    int ret = 0;
    size_t idx = 0;

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

I experimented with some different ways, including 'auto' return.

答案1

得分: 3

使用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, // 通过引用接受 r
             const std::source_location loc = std::source_location::current()) {
    try {
        r = v.at(idx); // 仅进行一次边界检查
    } catch (...) {
        // 如果使用 `printf`,可能需要 `std::fflush(stdout)` 以查看您的消息。改用 `fprintf(stderr, ...` 或 `std::cerr`。
        std::cerr << loc.file_name() << ':' << loc.line() << " 错误:std::vector "
                  << idx << " 超出范围,大小为 " << v.size() << '\n';
        throw;  // 重新抛出原始异常
    }
}

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 错误:std::vector 0 超出范围,大小为 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)
英文:

Using std::source_location and just checking the bounds once:

#include &lt;iostream&gt;
#include &lt;source_location&gt;
#include &lt;stdexcept&gt;
#include &lt;vector&gt;

template &lt;class T, class R&gt;
void vec_get(T v, R&amp; 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 &lt;&lt; loc.file_name() &lt;&lt; &#39;:&#39; &lt;&lt; loc.line() &lt;&lt; &quot; err std::vector &quot;
                  &lt;&lt; idx &lt;&lt; &quot; out of range size &quot; &lt;&lt; v.size() &lt;&lt; &#39;\n&#39;;
        throw;  // rethrow the original exception
    }
}

int main() {
    std::vector&lt;std::string&gt; v;
    size_t idx = 0;
    std::string s;

    vec_get(v, s, idx);

    std::cout &lt;&lt; s &lt;&lt; &#39;\n&#39;;
}

Possible output:

example.cpp:23 err std::vector 0 out of range size 0
terminate called after throwing an instance of &#39;std::out_of_range&#39;
  what():  vector::_M_range_check: __n (which is 0) &gt;= this-&gt;size() (which is 0) 

答案2

得分: 0

在C++20中,您可以使用std::source_location。尽管据我所知,只有gcc支持它。然后,您就不需要宏了。

实际上,我不知道您在返回元素时遇到了什么问题。您可以直接这样做 How to make a C++ wrapper to provide file and line information for a std::vector out of range message on terminal and abort

#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的必要。此外,如果要在程序终止时看到输出,必须确保在抛出异常之前刷新流。


然而,我允许自己质疑您的方法。与其试图通过无效的索引尝试访问元素,您应该确保索引在实际尝试访问元素之前是有效的。很少有情况下你不能这样做。我想不出除非索引来自外部输入,否则你可以实施一些输入的合理性检查,而不依赖于容器抛出一些与根本原因无关的out_of_range错误。

另一方面,有很多情况下,您不希望进行边界检查。例如,在循环中迭代元素将是一种浪费:

for (int i = 0; i < v.size(); ++i) vec_get(v, i); // 请不要这样做!
英文:

In c++20 you can use std::source_location. Though afaik only gcc supports it. Then you do not need the macro.

Actually I don't know what issue you encountered with returning the element. You can just do it How to make a C++ wrapper to provide file and line information for a std::vector out of range message on terminal and abort

#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;stdexcept&gt;
#include &lt;source_location&gt;
		
using namespace std;

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

int main() {
    std::vector&lt;std::string&gt; v;

    int ret = 0;
    size_t idx = 0;

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

Live Demo

As mentioned in comments, once you checked that the index is valid and throw your own exception there is no point in calling at. Also you have to make sure to flush the stream before throwing the exception if you want to see output also when the program terminates.


However, I do allow myself to question your approach. Instead of getting used to try accessing element via invalid index, you should rather make sure that indices are valid before actually trying to access the element. It is really rare that you cannot do that. From the top of my head the only instance I can imagine is when the index is from external input. And in this case you would implement some sanity checks on the input rather than relying on the container to throw some out_of_range error that tells nothing about the root cause.

On the other hand, there are many cases where you do not want bounds checking. For example it would be a waste in a loop iterating the elements:

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

huangapple
  • 本文由 发表于 2023年2月8日 22:03:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75386904.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定