From b0e3c5769a74f2d94fb644605247d4e9d186f486 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Wed, 3 Sep 2025 19:48:43 -0600 Subject: [PATCH] Remove dangerous result/option macros --- cpp/examples/app_service.cpp | 35 ++++----- cpp/examples/debug_proxy.cpp | 46 +++++------ cpp/examples/diagnosticsservice.cpp | 60 +++++++------- cpp/examples/idevice_id.cpp | 15 +--- cpp/examples/ideviceinfo.cpp | 43 +++------- cpp/examples/location_simulation.cpp | 54 ++++++------- cpp/include/idevice++/option.hpp | 50 ++++++------ cpp/include/idevice++/result.hpp | 113 ++++++++++++++++++++++----- 8 files changed, 215 insertions(+), 201 deletions(-) diff --git a/cpp/examples/app_service.cpp b/cpp/examples/app_service.cpp index c9cf136..730c889 100644 --- a/cpp/examples/app_service.cpp +++ b/cpp/examples/app_service.cpp @@ -44,12 +44,9 @@ int main(int argc, char** argv) { FfiError err; // 1) Connect to usbmuxd and pick first device - auto mux = UsbmuxdConnection::default_new(/*tag*/ 0); - if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); + auto mux = UsbmuxdConnection::default_new(/*tag*/ 0).expect("failed to connect to usbmuxd"); - auto devices_res = mux.unwrap().get_devices(); - if_let_err(devices_res, err, { die("failed to list devices", err); }); - auto& devices = devices_res.unwrap(); + auto devices = mux.get_devices().expect("failed to list devices"); if (devices.empty()) { std::cerr << "no devices connected\n"; return 1; @@ -73,35 +70,31 @@ int main(int argc, char** argv) { const uint32_t tag = 0; const std::string label = "app_service-jkcoxson"; - auto provider_res = - Provider::usbmuxd_new(std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); - if_let_err(provider_res, err, { die("failed to create provider", err); }); - auto& provider = provider_res.unwrap(); + auto provider = + Provider::usbmuxd_new(std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label) + .expect("failed to create provider"); // 3) CoreDeviceProxy - auto cdp = CoreDeviceProxy::connect(provider).unwrap_or_else( + auto cdp = CoreDeviceProxy::connect(provider).unwrap_or_else( [](FfiError e) -> CoreDeviceProxy { die("failed to connect CoreDeviceProxy", e); }); auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); // 4) Create software tunnel adapter (consumes proxy) - auto adapter = std::move(cdp).create_tcp_adapter(); - if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); + auto adapter = + std::move(cdp).create_tcp_adapter().expect("failed to create software tunnel adapter"); // 5) Connect adapter to RSD → ReadWrite stream - auto stream = adapter.unwrap().connect(rsd_port); - if_let_err(stream, err, { die("failed to connect RSD stream", err); }); + auto stream = adapter.connect(rsd_port).expect("failed to connect RSD stream"); // 6) RSD handshake (consumes stream) - auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); - if_let_err(rsd, err, { die("failed RSD handshake", err); }); + auto rsd = RsdHandshake::from_socket(std::move(stream)).expect("failed RSD handshake"); // 7) AppService over RSD (borrows adapter + handshake) - auto app = AppService::connect_rsd(adapter.unwrap(), rsd.unwrap()) - .unwrap_or_else([&](FfiError e) -> AppService { - die("failed to connect AppService", e); // never returns - }); + auto app = AppService::connect_rsd(adapter, rsd).unwrap_or_else([&](FfiError e) -> AppService { + die("failed to connect AppService", e); // never returns + }); // 8) Commands if (cmd == "list") { @@ -160,7 +153,7 @@ int main(int argc, char** argv) { } std::string bundle_id = argv[2]; - if_let_err(app.uninstall(bundle_id), err, { die("uninstall failed", err); }); + app.uninstall(bundle_id).expect("Uninstall failed"); std::cout << "Uninstalled " << bundle_id << "\n"; return 0; diff --git a/cpp/examples/debug_proxy.cpp b/cpp/examples/debug_proxy.cpp index 182b85d..a451cf8 100644 --- a/cpp/examples/debug_proxy.cpp +++ b/cpp/examples/debug_proxy.cpp @@ -34,18 +34,16 @@ static std::vector split_args(const std::string& line) { int main() { IdeviceFFI::FfiError err; - // 1) usbmuxd → pick first device - auto mux = IdeviceFFI::UsbmuxdConnection::default_new(/*tag*/ 0); - if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); + // 1) Connect to usbmuxd and pick first device + auto mux = UsbmuxdConnection::default_new(/*tag*/ 0).expect("failed to connect to usbmuxd"); - auto devices = mux.unwrap().get_devices(); - if_let_err(devices, err, { die("failed to list devices", err); }); - if (devices.unwrap().empty()) { + auto devices = mux.get_devices().expect("failed to list devices"); + if (devices.empty()) { std::cerr << "no devices connected\n"; return 1; } + auto& dev = (devices)[0]; - auto& dev = (devices.unwrap())[0]; auto udid = dev.get_udid(); if (udid.is_none()) { std::cerr << "device has no UDID\n"; @@ -58,39 +56,35 @@ int main() { } // 2) Provider via default usbmuxd addr - auto addr = IdeviceFFI::UsbmuxdAddr::default_new(); + auto addr = UsbmuxdAddr::default_new(); - const uint32_t tag = 0; - const std::string label = "debug-proxy-jkcoxson"; - auto provider = IdeviceFFI::Provider::usbmuxd_new( - std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); - if_let_err(provider, err, { die("failed to create provider", err); }); + const uint32_t tag = 0; + const std::string label = "app_service-jkcoxson"; + + auto provider = + Provider::usbmuxd_new(std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label) + .expect("failed to create provider"); // 3) CoreDeviceProxy - auto cdp = CoreDeviceProxy::connect(provider.unwrap()) - .unwrap_or_else([](FfiError e) -> CoreDeviceProxy { - die("failed to connect CoreDeviceProxy", e); - }); + auto cdp = CoreDeviceProxy::connect(provider).unwrap_or_else( + [](FfiError e) -> CoreDeviceProxy { die("failed to connect CoreDeviceProxy", e); }); auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); // 4) Create software tunnel adapter (consumes proxy) - auto adapter = std::move(cdp).create_tcp_adapter(); - if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); + auto adapter = + std::move(cdp).create_tcp_adapter().expect("failed to create software tunnel adapter"); // 5) Connect adapter to RSD → ReadWrite stream - auto stream = adapter.unwrap().connect(rsd_port); - if_let_err(stream, err, { die("failed to connect RSD stream", err); }); + auto stream = adapter.connect(rsd_port).expect("failed to connect RSD stream"); // 6) RSD handshake (consumes stream) - auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); - if_let_err(rsd, err, { die("failed RSD handshake", err); }); + auto rsd = RsdHandshake::from_socket(std::move(stream)).expect("failed RSD handshake"); // 6) DebugProxy over RSD - auto dbg_res = IdeviceFFI::DebugProxy::connect_rsd(adapter.unwrap(), rsd.unwrap()); - if_let_err(dbg_res, err, { die("failed to connect DebugProxy", err); }); - auto& dbg = dbg_res.unwrap(); + auto dbg = + IdeviceFFI::DebugProxy::connect_rsd(adapter, rsd).expect("failed to connect DebugProxy"); std::cout << "Shell connected! Type 'exit' to quit.\n"; for (;;) { diff --git a/cpp/examples/diagnosticsservice.cpp b/cpp/examples/diagnosticsservice.cpp index bcf2eb0..ffde043 100644 --- a/cpp/examples/diagnosticsservice.cpp +++ b/cpp/examples/diagnosticsservice.cpp @@ -29,18 +29,16 @@ int main() { idevice_init_logger(Debug, Disabled, NULL); FfiError err; - // 1) usbmuxd → pick first device - auto mux = IdeviceFFI::UsbmuxdConnection::default_new(/*tag*/ 0); - if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); + // 1) Connect to usbmuxd and pick first device + auto mux = UsbmuxdConnection::default_new(/*tag*/ 0).expect("failed to connect to usbmuxd"); - auto devices = mux.unwrap().get_devices(); - if_let_err(devices, err, { die("failed to list devices", err); }); - if (devices.unwrap().empty()) { + auto devices = mux.get_devices().expect("failed to list devices"); + if (devices.empty()) { std::cerr << "no devices connected\n"; return 1; } + auto& dev = (devices)[0]; - auto& dev = (devices.unwrap())[0]; auto udid = dev.get_udid(); if (udid.is_none()) { std::cerr << "device has no UDID\n"; @@ -53,58 +51,54 @@ int main() { } // 2) Provider via default usbmuxd addr - auto addr = IdeviceFFI::UsbmuxdAddr::default_new(); + auto addr = UsbmuxdAddr::default_new(); - const uint32_t tag = 0; - const std::string label = "debug-proxy-jkcoxson"; - auto provider = IdeviceFFI::Provider::usbmuxd_new( - std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); - if_let_err(provider, err, { die("failed to create provider", err); }); + const uint32_t tag = 0; + const std::string label = "app_service-jkcoxson"; + + auto provider = + Provider::usbmuxd_new(std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label) + .expect("failed to create provider"); // 3) CoreDeviceProxy - auto cdp = CoreDeviceProxy::connect(provider.unwrap()) - .unwrap_or_else([](FfiError e) -> CoreDeviceProxy { - die("failed to connect CoreDeviceProxy", e); - }); + auto cdp = CoreDeviceProxy::connect(provider).unwrap_or_else( + [](FfiError e) -> CoreDeviceProxy { die("failed to connect CoreDeviceProxy", e); }); auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); // 4) Create software tunnel adapter (consumes proxy) - auto adapter = std::move(cdp).create_tcp_adapter(); - if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); + auto adapter = + std::move(cdp).create_tcp_adapter().expect("failed to create software tunnel adapter"); // 5) Connect adapter to RSD → ReadWrite stream - auto stream = adapter.unwrap().connect(rsd_port); - if_let_err(stream, err, { die("failed to connect RSD stream", err); }); + auto stream = adapter.connect(rsd_port).expect("failed to connect RSD stream"); // 6) RSD handshake (consumes stream) - auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); - if_let_err(rsd, err, { die("failed RSD handshake", err); }); + auto rsd = RsdHandshake::from_socket(std::move(stream)).expect("failed RSD handshake"); - // 6) DebugProxy over RSD - auto diag = DiagnosticsService::connect_rsd(adapter.unwrap(), rsd.unwrap()); - if_let_err(diag, err, { die("failed to connect DebugProxy", err); }); + // 7) DebugProxy over RSD + auto diag = + DiagnosticsService::connect_rsd(adapter, rsd).expect("failed to connect DebugProxy"); std::cout << "Getting sysdiagnose, this takes a while! iOS is slow...\n"; - auto cap = diag.unwrap().capture_sysdiagnose(/*dry_run=*/false); - if_let_err(cap, err, { die("capture_sysdiagnose failed", err); }); + auto cap = diag.capture_sysdiagnose(/*dry_run=*/false).expect("capture_sysdiagnose failed"); - std::cout << "Got sysdiagnose! Saving to file: " << cap.unwrap().preferred_filename << "\n"; + std::cout << "Got sysdiagnose! Saving to file: " << cap.preferred_filename << "\n"; // 7) Stream to file with progress - std::ofstream out(cap.unwrap().preferred_filename, std::ios::binary); + std::ofstream out(cap.preferred_filename, std::ios::binary); if (!out) { std::cerr << "failed to open output file\n"; return 1; } std::size_t written = 0; - const std::size_t total = cap.unwrap().expected_length; + const std::size_t total = cap.expected_length; for (;;) { - auto chunk = cap.unwrap().stream.next_chunk(); + auto chunk = cap.stream.next_chunk(); match_result( chunk, res, @@ -128,6 +122,6 @@ int main() { } out.flush(); - std::cout << "\nDone! Saved to " << cap.unwrap().preferred_filename << "\n"; + std::cout << "\nDone! Saved to " << cap.preferred_filename << "\n"; return 0; } diff --git a/cpp/examples/idevice_id.cpp b/cpp/examples/idevice_id.cpp index c99f254..79bcd5e 100644 --- a/cpp/examples/idevice_id.cpp +++ b/cpp/examples/idevice_id.cpp @@ -4,19 +4,10 @@ #include int main() { - auto u = IdeviceFFI::UsbmuxdConnection::default_new(0); - if_let_err(u, e, { - std::cerr << "failed to connect to usbmuxd"; - std::cerr << e.message; - }); + auto u = IdeviceFFI::UsbmuxdConnection::default_new(0).expect("failed to connect to usbmuxd"); + auto devices = u.get_devices().expect("failed to get devices from usbmuxd"); - auto devices = u.unwrap().get_devices(); - if_let_err(devices, e, { - std::cerr << "failed to get devices from usbmuxd"; - std::cerr << e.message; - }); - - for (IdeviceFFI::UsbmuxdDevice& d : devices.unwrap()) { + for (IdeviceFFI::UsbmuxdDevice& d : devices) { auto udid = d.get_udid(); if (udid.is_none()) { std::cerr << "failed to get udid"; diff --git a/cpp/examples/ideviceinfo.cpp b/cpp/examples/ideviceinfo.cpp index d93756d..9642db0 100644 --- a/cpp/examples/ideviceinfo.cpp +++ b/cpp/examples/ideviceinfo.cpp @@ -9,21 +9,9 @@ int main() { idevice_init_logger(Debug, Disabled, NULL); - auto u_res = IdeviceFFI::UsbmuxdConnection::default_new(0); - if_let_err(u_res, e, { - std::cerr << "failed to connect to usbmuxd"; - std::cerr << e.message; - return 1; - }); - auto& u = u_res.unwrap(); + auto u = IdeviceFFI::UsbmuxdConnection::default_new(0).expect("failed to connect to usbmuxd"); - auto devices_res = u.get_devices(); - if_let_err(devices_res, e, { - std::cerr << "failed to get devices from usbmuxd"; - std::cerr << e.message; - return 1; - }); - auto devices = std::move(devices_res).unwrap(); + auto devices = u.get_devices().expect("failed to get devices from usbmuxd"); if (devices.empty()) { std::cerr << "no devices connected"; @@ -44,28 +32,15 @@ int main() { return 1; } - IdeviceFFI::UsbmuxdAddr addr = IdeviceFFI::UsbmuxdAddr::default_new(); - auto prov_res = IdeviceFFI::Provider::usbmuxd_new( - std::move(addr), /*tag*/ 0, udid.unwrap(), id.unwrap(), "reeeeeeeee"); - if_let_err(prov_res, e, { - std::cerr << "provider failed: " << e.message << "\n"; - return 1; - }); - auto& prov = prov_res.unwrap(); + IdeviceFFI::UsbmuxdAddr addr = IdeviceFFI::UsbmuxdAddr::default_new(); + auto prov = IdeviceFFI::Provider::usbmuxd_new( + std::move(addr), /*tag*/ 0, udid.unwrap(), id.unwrap(), "reeeeeeeee") + .expect("Failed to create usbmuxd provider"); - auto client_res = IdeviceFFI::Lockdown::connect(prov); - if_let_err(client_res, e, { - std::cerr << "lockdown connect failed: " << e.message << "\n"; - return 1; - }); - auto& client = client_res.unwrap(); + auto client = IdeviceFFI::Lockdown::connect(prov).expect("lockdown connect failed"); - auto pf = prov.get_pairing_file(); - if_let_err(pf, e, { - std::cerr << "failed to get pairing file: " << e.message << "\n"; - return 1; - }); - client.start_session(pf.unwrap()); + auto pf = prov.get_pairing_file().expect("failed to get pairing file"); + client.start_session(pf).expect("failed to start session"); auto values = client.get_value(NULL, NULL); match_result( diff --git a/cpp/examples/location_simulation.cpp b/cpp/examples/location_simulation.cpp index 9211ae1..b458faa 100644 --- a/cpp/examples/location_simulation.cpp +++ b/cpp/examples/location_simulation.cpp @@ -41,18 +41,16 @@ int main(int argc, char** argv) { return 2; } - // 1) usbmuxd → pick first device - auto mux = IdeviceFFI::UsbmuxdConnection::default_new(/*tag*/ 0); - if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); + // 1) Connect to usbmuxd and pick first device + auto mux = UsbmuxdConnection::default_new(/*tag*/ 0).expect("failed to connect to usbmuxd"); - auto devices = mux.unwrap().get_devices(); - if_let_err(devices, err, { die("failed to list devices", err); }); - if (devices.unwrap().empty()) { + auto devices = mux.get_devices().expect("failed to list devices"); + if (devices.empty()) { std::cerr << "no devices connected\n"; return 1; } + auto& dev = (devices)[0]; - auto& dev = (devices.unwrap())[0]; auto udid = dev.get_udid(); if (udid.is_none()) { std::cerr << "device has no UDID\n"; @@ -65,58 +63,52 @@ int main(int argc, char** argv) { } // 2) Provider via default usbmuxd addr - auto addr = IdeviceFFI::UsbmuxdAddr::default_new(); + auto addr = UsbmuxdAddr::default_new(); - const uint32_t tag = 0; - const std::string label = "debug-proxy-jkcoxson"; - auto provider = IdeviceFFI::Provider::usbmuxd_new( - std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); - if_let_err(provider, err, { die("failed to create provider", err); }); + const uint32_t tag = 0; + const std::string label = "app_service-jkcoxson"; + + auto provider = + Provider::usbmuxd_new(std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label) + .expect("failed to create provider"); // 3) CoreDeviceProxy - auto cdp = CoreDeviceProxy::connect(provider.unwrap()) - .unwrap_or_else([](FfiError e) -> CoreDeviceProxy { - die("failed to connect CoreDeviceProxy", e); - }); + auto cdp = CoreDeviceProxy::connect(provider).unwrap_or_else( + [](FfiError e) -> CoreDeviceProxy { die("failed to connect CoreDeviceProxy", e); }); auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); // 4) Create software tunnel adapter (consumes proxy) - auto adapter = std::move(cdp).create_tcp_adapter(); - if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); + auto adapter = + std::move(cdp).create_tcp_adapter().expect("failed to create software tunnel adapter"); // 5) Connect adapter to RSD → ReadWrite stream - auto stream = adapter.unwrap().connect(rsd_port); - if_let_err(stream, err, { die("failed to connect RSD stream", err); }); + auto stream = adapter.connect(rsd_port).expect("failed to connect RSD stream"); // 6) RSD handshake (consumes stream) - auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); - if_let_err(rsd, err, { die("failed RSD handshake", err); }); + auto rsd = RsdHandshake::from_socket(std::move(stream)).expect("failed RSD handshake"); // 8) RemoteServer over RSD (borrows adapter + handshake) - auto rs = RemoteServer::connect_rsd(adapter.unwrap(), rsd.unwrap()); - if_let_err(rs, err, { die("failed to connect RemoteServer", err); }); + auto rs = RemoteServer::connect_rsd(adapter, rsd).expect("failed to connect to RemoteServer"); // 9) LocationSimulation client (borrows RemoteServer) - auto sim_res = LocationSimulation::create(rs.unwrap()); - if_let_err(sim_res, err, { die("failed to create LocationSimulation client", err); }); - auto& sim = sim_res.unwrap(); + auto sim = LocationSimulation::create(rs).expect("failed to create LocationSimulation client"); if (do_clear) { - if_let_err(sim.clear(), err, { die("clear failed", err); }); + sim.clear().expect("clear failed"); std::cout << "Location cleared!\n"; return 0; } // set path - if_let_err(sim.set(lat.unwrap(), lon.unwrap()), err, { die("set failed", err); }); + sim.set(lat.unwrap(), lon.unwrap()).expect("set failed"); std::cout << "Location set to (" << lat.unwrap() << ", " << lon.unwrap() << ")\n"; std::cout << "Press Ctrl-C to stop\n"; // keep process alive like the Rust example for (;;) { - if_let_err(sim.set(lat.unwrap(), lon.unwrap()), err, { die("set failed", err); }); + sim.set(lat.unwrap(), lon.unwrap()).expect("set failed"); std::this_thread::sleep_for(std::chrono::seconds(3)); } } diff --git a/cpp/include/idevice++/option.hpp b/cpp/include/idevice++/option.hpp index b522b92..be25570 100644 --- a/cpp/include/idevice++/option.hpp +++ b/cpp/include/idevice++/option.hpp @@ -8,7 +8,6 @@ #pragma once -#include #include #include #include @@ -106,6 +105,8 @@ template class Option { // unwrap_or / unwrap_or_else T unwrap_or(T default_value) const& { return has_ ? *ptr() : std::move(default_value); } T unwrap_or(T default_value) && { return has_ ? std::move(*ptr()) : std::move(default_value); } + T unwrap_or(const T& default_value) const& { return has_ ? *ptr() : default_value; } + T unwrap_or(T&& default_value) const& { return has_ ? *ptr() : std::move(default_value); } template T unwrap_or_else(F&& f) const& { return has_ ? *ptr() : static_cast(f()); @@ -123,6 +124,16 @@ template class Option { } return Option(None); } + + template + auto map(F&& f) && -> Option::type> { + using U = typename std::decay::type; + if (has_) { + // Move the value into the function + return Option(f(std::move(*ptr()))); + } + return Option(None); + } }; // Helpers @@ -131,21 +142,17 @@ template inline Option::type> Some(T&& v) { } inline Option Some() = delete; // no Option -// template inline Option None() { -// return Option(none); -// } // still needs T specified - -// Prefer this at call sites (lets return-type drive the type): -// return none; - -#define match_option(opt, SOME, NONE) \ - if ((opt).is_some()) { \ - auto&& SOME = (opt).unwrap(); -#define or_else \ - } \ - else { \ - NONE; \ - } +#define match_option(opt, some_name, some_block, none_block) \ + /* NOTE: you may return in a block, but not break/continue */ \ + do { \ + auto&& _option_val = (opt); \ + if (_option_val.is_some()) { \ + auto&& some_name = _option_val.unwrap(); \ + some_block \ + } else { \ + none_block \ + } \ + } while (0) // --- Option helpers: if_let_some / if_let_some_move / if_let_none --- @@ -154,6 +161,7 @@ inline Option Some() = delete; // no Option /* Bind a reference to the contained value if Some(...) */ #define if_let_some(expr, name, block) \ + /* NOTE: you may return in a block, but not break/continue */ \ do { \ auto _opt_unique(_opt_) = (expr); \ if (_opt_unique(_opt_).is_some()) { \ @@ -164,6 +172,7 @@ inline Option Some() = delete; // no Option /* Move the contained value out (consumes the Option) if Some(...) */ #define if_let_some_move(expr, name, block) \ + /* NOTE: you may return in a block, but not break/continue */ \ do { \ auto _opt_unique(_opt_) = (expr); \ if (_opt_unique(_opt_).is_some()) { \ @@ -172,13 +181,4 @@ inline Option Some() = delete; // no Option } \ } while (0) -/* Run a block if the option is None */ -#define if_let_none(expr, block) \ - do { \ - auto _opt_unique(_opt_) = (expr); \ - if (_opt_unique(_opt_).is_none()) { \ - block \ - } \ - } while (0) - } // namespace IdeviceFFI diff --git a/cpp/include/idevice++/result.hpp b/cpp/include/idevice++/result.hpp index fd4872e..0e545bb 100644 --- a/cpp/include/idevice++/result.hpp +++ b/cpp/include/idevice++/result.hpp @@ -76,6 +76,57 @@ template class Result { } } + // Copy Assignment + Result& operator=(const Result& other) { + // Prevent self-assignment + if (this == &other) { + return *this; + } + + // Destroy the current value + if (is_ok_) { + ok_value_.~T(); + } else { + err_value_.~E(); + } + + is_ok_ = other.is_ok_; + + // Construct the new value + if (is_ok_) { + new (&ok_value_) T(other.ok_value_); + } else { + new (&err_value_) E(other.err_value_); + } + + return *this; + } + + // Move Assignment + Result& operator=(Result&& other) noexcept { + if (this == &other) { + return *this; + } + + // Destroy the current value + if (is_ok_) { + ok_value_.~T(); + } else { + err_value_.~E(); + } + + is_ok_ = other.is_ok_; + + // Construct the new value by moving + if (is_ok_) { + new (&ok_value_) T(std::move(other.ok_value_)); + } else { + new (&err_value_) E(std::move(other.err_value_)); + } + + return *this; + } + bool is_ok() const { return is_ok_; } bool is_err() const { return !is_ok_; } @@ -132,6 +183,32 @@ template class Result { T unwrap_or(T&& default_value) const { return is_ok_ ? ok_value_ : std::move(default_value); } + T expect(const char* message) && { + if (is_err()) { + std::fprintf(stderr, "Fatal (expect) error: %s\n", message); + std::terminate(); + } + return std::move(ok_value_); + } + + // Returns a mutable reference from an lvalue Result + T& expect(const char* message) & { + if (is_err()) { + std::fprintf(stderr, "Fatal (expect) error: %s\n", message); + std::terminate(); + } + return ok_value_; + } + + // Returns a const reference from a const lvalue Result + const T& expect(const char* message) const& { + if (is_err()) { + std::fprintf(stderr, "Fatal (expect) error: %s\n", message); + std::terminate(); + } + return ok_value_; + } + template T unwrap_or_else(F&& f) & { return is_ok_ ? ok_value_ : static_cast(f(err_value_)); } @@ -206,26 +283,24 @@ template class Result { } return err_value_; } + + void expect(const char* message) const { + if (is_err()) { + std::fprintf(stderr, "Fatal (expect) error: %s\n", message); + std::terminate(); + } + } }; #define match_result(res, ok_name, ok_block, err_name, err_block) \ - if ((res).is_ok()) { \ - auto&& ok_name = (res).unwrap(); \ - ok_block \ - } else { \ - auto&& err_name = (res).unwrap_err(); \ - err_block \ - } - -#define if_let_err(res, name, block) \ - if ((res).is_err()) { \ - auto&& name = (res).unwrap_err(); \ - block \ - } - -#define if_let_ok(res, name, block) \ - if ((res).is_ok()) { \ - auto&& name = (res).unwrap(); \ - block \ - } + do { \ + auto&& _result_val = (res); \ + if (_result_val.is_ok()) { \ + auto&& ok_name = _result_val.unwrap(); \ + ok_block \ + } else { \ + auto&& err_name = _result_val.unwrap_err(); \ + err_block \ + } \ + } while (0) } // namespace IdeviceFFI