英文:
How to use std::expected in conjunction with std::transform?
问题
我有一个FlatBuffer。这个FlatBuffer有一个vector<T>
,我想要使用std::transform
将其转换为一个std::vector<U>
。
可能会出现T
包含无效值的情况,因为它们是将普通整数转换为枚举值。在这种情况下,我想中止整个转换过程。
目前,我在std::transform
的lambda函数中抛出异常,然后在调用转换函数的函数中捕获它。
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;
});
我如何使用std::expected
来建模这个问题?一个std::vector<std::expected<T,E>>
似乎有点愚蠢。
英文:
I have a flatbuffer. This flatbuffer has a vector<T>
that I want to transform using std::transform
into a std::vector<U>
.
It is possible that T
can contain values that are not valid - because they are a plain integer converted to an enum. In this case, I want to abort the whole transform process.
Currently I throw an exception within the lambda of std::transform
and catch it in the function that calls the transform function.
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;
});
How would I model this with std::expected
? A std::vector<std::expected<T,E>>
seems kinda silly.
答案1
得分: 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
只接收带有 std::string
的 throw
。
您可以像这样使用它:
#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
希望这有所帮助。
英文:
If the transformation is to be aborted at the first unexpected value, an exception in the lambda is the right choice. It is the only way to cancel a std::transform
.
What you want as a result is probably std::expected<std::vector<U>, std::string>
. To do this, you put the std::transform
call into a function that returns a corresponding 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));
}
Note that you can still throw other exceptions normally. The std::expected
here only receives throw
's with std::string
.
You can use it like this:
#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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论