C++ 在奇怪的情况下跳过内部捕捉
我在一个我一直在研究的代码库中发现了一个非常奇怪的bug,直到最近我才能够从中分离并创建一些可复制的东西。错误在于当C++ 在奇怪的情况下跳过内部捕捉,c++,try-catch,c++17,C++,Try Catch,C++17,我在一个我一直在研究的代码库中发现了一个非常奇怪的bug,直到最近我才能够从中分离并创建一些可复制的东西。错误在于当throw\u threshold为偶数时,跳过simulate\u container\u init中的捕获throw\u threshold用于ex\u trigger中,以模拟在容器构造或分配等情况下复制对象时的抛出,以进行单元测试 我最初认为这是一个MSVC编译器错误(在撰写本文时为14.16),但在GCC 7.1和Clang 3.9.1(几乎是支持我的示例的最古老的版本)
throw\u threshold
为偶数时,跳过simulate\u container\u init
中的捕获throw\u threshold
用于ex\u trigger
中,以模拟在容器构造或分配等情况下复制对象时的抛出,以进行单元测试
我最初认为这是一个MSVC编译器错误(在撰写本文时为14.16),但在GCC 7.1和Clang 3.9.1(几乎是支持我的示例的最古老的版本)上成功地复制了它之后,我不确定该如何处理它,当throw\u threshold
为奇数时,代码显示正确且功能正常
#include <cstdint>
#include <atomic>
#include <memory>
#include <random>
#include <iostream>
#include <exception>
#include <type_traits>
// Used to trigger an exception after a number of constructions have occurred.
struct ex_trigger
{
private:
static std::atomic<uint32_t> ctor_count;
static std::atomic<uint32_t> throw_threshold;
public:
static inline void set_throw_threshold(uint32_t value) noexcept
{
throw_threshold.store(value, std::memory_order_release);
}
std::atomic<uint32_t> value;
inline ex_trigger(const ex_trigger& source) :
value(source.value.load(std::memory_order_acquire))
{
if (ctor_count.load(std::memory_order_acquire) >=
throw_threshold.load(std::memory_order_acquire)) {
throw std::logic_error("test");
}
ctor_count.fetch_add(1);
}
inline ex_trigger(uint32_t value) noexcept :
value(value)
{
ctor_count.fetch_add(1);
}
};
std::atomic<uint32_t> ex_trigger::ctor_count;
std::atomic<uint32_t> ex_trigger::throw_threshold;
// Simulates the construction of a container by copying an initializer list.
template<class T>
inline void simulate_container_ctor(std::initializer_list<T> values) {
// Intentionally leaked to simplify this example.
// Alignment of T is completely ignored for simplicity.
auto sim_data = static_cast<T*>(
::operator new(sizeof(T) * values.size()));
for (auto value : values) {
if constexpr (std::is_nothrow_copy_constructible_v<T>) {
new (sim_data++) T(value);
} else {
try {
new (sim_data++) T(value);
} catch (...) {
// Placeholder for cleanup code which is sometimes skipped.
std::cout << "caught [inner]\n";
throw;
}
}
}
}
int main()
{
// The "inner" catch handler within simulate_container_init will be skipped when the argument
// to set_throw_threshold is even, but otherwise appears to work correctly when it's odd. Note
// that the argument must be between 10-20 for an exception to be triggered in this example.
ex_trigger::set_throw_threshold(11);
try {
simulate_container_ctor({
ex_trigger(1),
ex_trigger(2),
ex_trigger(3),
ex_trigger(4),
ex_trigger(5),
ex_trigger(6),
ex_trigger(7),
ex_trigger(8),
ex_trigger(9),
ex_trigger(10)
});
} catch (const std::logic_error&) {
std::cout << "caught [outer]\n";
}
std::cin.get();
return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
//用于在多次构造发生后触发异常。
结构exu触发器
{
私人:
静态标准::原子计数;
静态std::原子抛出_阈值;
公众:
静态内联无效集\u抛出\u阈值(uint32\u t值)无异常
{
throw\u threshold.store(值、标准::内存\u顺序\u释放);
}
std::原子值;
内联ex_触发器(常量ex_触发器和源):
值(source.value.load(std::memory\u order\u acquire))
{
如果(ctor_count.load(std::内存_order_acquire)>=
throw\u threshold.load(标准::内存\u顺序\u获取)){
抛出标准::逻辑_错误(“测试”);
}
ctor_count.fetch_add(1);
}
内联ex_触发器(uint32_t值)无异常:
价值(价值)
{
ctor_count.fetch_add(1);
}
};
std::原子ex_触发器::ctor_计数;
std::原子ex_触发器::抛出_阈值;
//通过复制初始值设定项列表来模拟容器的构造。
模板
内联无效模拟容器(标准::初始值设定项列表值){
//故意泄露以简化此示例。
//为了简单起见,完全忽略了T的对齐。
自动模拟数据=静态模拟(
::运算符new(sizeof(T)*values.size());
用于(自动值:值){
if constexpr(std::is_nothrow_copy_constructible_v){
新(sim_data++)T(值);
}否则{
试一试{
新(sim_data++)T(值);
}捕获(…){
//清除代码的占位符,有时会被跳过。
std::cout问题是(auto-value:values){
copy构造一个临时的ex\u触发器
,该触发器将在内部异常处理程序之外抛出异常。解决方法是迭代(auto-const&value:values){的引用{copy构造一个临时的ex_触发器
,该触发器将在内部异常处理程序外部引发异常。修复方法是迭代(auto const&value:values)的引用{
有时候真正需要的是一双新鲜的眼睛。谢谢你的帮助。有时候真正需要的是一双新鲜的眼睛。谢谢你的帮助。