提问人:Raildex 提问时间:3/12/2023 更新时间:3/12/2023 访问量:314
如何将 std::expected 与 std::transform 结合使用?
How to use std::expected in conjunction with std::transform?
问:
我有一个平坦的缓冲区。这个 flatbuffer 有一个我想用它转换为 .vector<T>
std::transform
std::vector<U>
可能包含无效的值 - 因为它们是转换为枚举的纯整数。在这种情况下,我想中止整个转换过程。T
目前,我在 lambda 中抛出一个异常,并在调用 transform 函数的函数中捕获它。std::transform
std::transform(deserialized_level.rooms.cbegin(), deserialized_level.rooms.cend(), constant_level_data.rooms.begin(),
[&out_progress, &deserialized_rooms, &deserialized_level](const std::unique_ptr<room_t>& deserialized_room) {
auto r = room_info{};
r.position = int3(deserialized_room->position->x, deserialized_room->position->y, deserialized_room->position->z);
r.sector_width = (unsigned char)(deserialized_room->sector_count_x);
r.sector_length = (unsigned char)(deserialized_room->sector_count_z);
auto sector_dimension = sector_size::normal;
switch(deserialized_room->square_size) {
case 256:
sector_dimension = sector_size::smallest;
break;
case 512:
sector_dimension = sector_size::smaller;
break;
case 1024:
sector_dimension = sector_size::normal;
break;
case 2048:
sector_dimension = sector_size::large;
break;
case 4096:
sector_dimension = sector_size::very_large;
break;
case 8192:
sector_dimension = sector_size::huge;
break;
case 16384:
sector_dimension = sector_size::gigantic;
break;
default:
throw std::runtime_error("Unknown sector size detected!" + std::to_string(deserialized_room->square_size));
}
//...//
return r;
});
我将如何对此进行建模?A似乎有点傻。std::expected
std::vector<std::expected<T,E>>
答:
1赞
Benjamin Buch
3/12/2023
#1
如果要在第一个意外值处中止转换,则 lambda 中的异常是正确的选择。这是取消 .std::transform
结果你想要的可能是.为此,将调用放入返回相应 .std::expected<std::vector<U>, std::string>
std::transform
std::expected
#include <algorithm>
#include <expected>
#include <string>
#include <vector>
struct room { int size; };
struct room_info { std::string category;};
std::expected<std::vector<room_info>, std::string>
info_of(std::vector<room> const& rooms) try {
std::vector<room_info> infos;
infos.reserve(rooms.size());
std::ranges::transform(rooms, std::back_inserter(infos),
[](room const& r) -> room_info{
using namespace std::literals;
switch(r.size) {
case 256:
return {"smallest"s};
case 1024:
return {"normal"s};
case 4096:
return {"very_large"s};
case 16384:
return {"gigantic"s};
default:
throw "invalid room size"s;
}
});
return infos;
} catch (std::string error) {
return std::unexpected(std::move(error));
}
请注意,您仍然可以正常抛出其他异常。这里只接收带有 的 。std::expected
throw
std::string
你可以像这样使用它:
#include <iostream>
void print(std::expected<std::vector<room_info>, std::string> const& info) {
if(info) {
auto const& list = info.value();
std::cout << "[";
if(!list.empty()) {
std::cout << list[0].category;
for(std::size_t i = 1; i < list.size(); ++i) {
std::cout << ", " << list[i].category;
}
}
std::cout << "]\n";
} else {
std::cout << info.error() << '\n';
}
}
int main() {
print(info_of({{256}, {16384}, {1024}}));
print(info_of({{256}, {16385}, {1024}}));
}
[smallest, gigantic, normal]
invalid room size
评论
0赞
Paul Sanders
3/13/2023
这些本身都没有错,但是让异常传播到您想要处理的程度不是很有意义吗?
0赞
Benjamin Buch
3/13/2023
@PaulSanders 不,你不能这么说。这取决于您希望如何应对相应的问题。这个例子很好地表明,异常和 std::expected 是甚至可以一起使用的正交技术。解析错误经常发生,因此在正常的控制流中处理。但是,lambda 也可能抛出bad_alloc。这种情况很少发生,因此完全通过异常处理。
0赞
Paul Sanders
3/13/2023
这一论点缺乏实质内容。,特别是,趋向于终末期。std::bad_alloc
0赞
Benjamin Buch
3/13/2023
@PaulSanders 是的,当然是一个非常糟糕的例子。只需假设任何其他例外。之所以命名,只是因为它是上述代码中唯一可能发生的其他事情。但这仅仅是由于最小示例的简单性。更复杂的转换 lambda 代码可能会引发其他异常 ;-)bad_alloc
bad_alloc
评论
In this case, I want to abort the whole transform process.