// leaf_mutable_block.cpp #include #include #include #include #include #include namespace leaf = boost::leaf; struct error_tag {}; struct open_error {}; struct write_error {}; struct diagnostic_context { std::string file; std::optional user; std::optional privileges; std::optional chunk; std::optional chunk_size; std::optional timestamp; }; leaf::result write_chunk(int chunk, std::size_t size) { if (chunk == 3) return leaf::new_error(write_error{}); return {}; } leaf::result process_file(std::string path, std::string user) { error_tag tag; diagnostic_context diag; diag.file = path; diag.user = std::move(user); diag.privileges = 0600; [[maybe_unused]] auto tag_ctx = leaf::on_error(tag); // on_error captures a snapshot of diag, not a reference, so any // fields mutated later would be invisible unless re-registered. [[maybe_unused]] auto diag_ctx = leaf::on_error(diag); if (diag.file.empty()) return leaf::new_error(open_error{}); for (int i = 0; i != 5; ++i) { diag.chunk = i; diag.chunk_size = 4096; diag.timestamp = std::time(nullptr); // Re-registering publishes the updated snapshot; the newest // registration wins for the handler. [[maybe_unused]] auto refresh = leaf::on_error(diag); auto r = write_chunk(i, 4096); if (!r) return r; } return {}; } int main() { return leaf::try_handle_all( []() -> leaf::result { BOOST_LEAF_CHECK(process_file("data.bin", "alice")); return 0; }, [](error_tag const&, diagnostic_context const& d) { // d is the newest snapshot taken before the error was raised, // which is why chunk/chunk_size/timestamp are filled in here. std::cerr << "operation failed: file=" << d.file; if (d.user) std::cerr << ", user=" << *d.user; if (d.privileges) std::cerr << ", privileges=" << *d.privileges; if (d.chunk) std::cerr << ", chunk=" << *d.chunk; if (d.chunk_size) std::cerr << ", chunk_size=" << *d.chunk_size; if (d.timestamp) std::cerr << ", time=" << *d.timestamp; std::cerr << '\n'; return 1; }, [] { std::cerr << "operation failed before diagnostic context existed\n"; return 2; } ); }