我在我正在研究的代码库中发现了一个非常奇怪的错误,直到最近我才能够隔离并创建可复制的东西。错误是simulate_container_init
偶数时跳过throw_threshold
内部的捕获量。 throw_threshold
中的ex_trigger
用于模拟在容器构造或出于单元测试目的的情况下复制时抛出的对象。
我本来以为这是MSVC编译器错误(在撰写本文时为14.16),但是在GCC 7.1和Clang 3.9.1(几乎每个支持我的示例的最早的)中成功重现之后,我不知道该怎么做,因为当throw_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;
}
throw_threshold
为偶数时,输出为(不正确):
抓住[外部]
throw_threshold
为奇数时,输出为(预期):
抓住[内部]
抓住[外层]
我花了无数小时来调试和尝试不同的方法,但似乎我缺少了一些东西。任何有助于理解这一点的方法将不胜感激。
答案 0 :(得分:2)
问题是JFrame
副本构造了一个临时for (auto value : values) {
,它将在内部异常处理程序之外引发异常。解决办法是遍历引用ex_trigger