Skip to content
This repository was archived by the owner on Apr 29, 2026. It is now read-only.

Commit 2dc9c2b

Browse files
committed
Enhance error handling and refactor C API for improved robustness
- Added new error handling functions and updated existing ones to use error codes instead of exceptions, enhancing the stability of the C API. - Refactored memory allocation in `dup_cstr` and other functions to handle out-of-memory scenarios gracefully. - Introduced `clear_error` and improved error reporting mechanisms across various functions to ensure consistent error management. - Updated function signatures to return status or optional types, improving clarity and usability. - Enhanced the `from_experiment` and `load` functions to provide better error feedback and prevent crashes due to invalid inputs.
1 parent ef99f0a commit 2dc9c2b

11 files changed

Lines changed: 1566 additions & 613 deletions

File tree

include/phy_engine/phy_lab_wrapper/auto_layout/auto_layout.h

Lines changed: 338 additions & 86 deletions
Large diffs are not rendered by default.

include/phy_engine/phy_lab_wrapper/c_api.h

Lines changed: 61 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "physicslab.h"
44

55
#include <cstring>
6+
#include <new>
67
#include <string>
78

89
// Minimal C API for embedding / wasm bindings.
@@ -13,10 +14,12 @@ namespace phy_engine::phy_lab_wrapper::c_api::detail
1314
inline thread_local std::string last_error;
1415

1516
inline void set_error(std::string msg) { last_error = std::move(msg); }
17+
inline void clear_error() noexcept { last_error.clear(); }
1618

1719
inline char* dup_cstr(std::string const& s)
1820
{
19-
auto* out = new char[s.size() + 1];
21+
auto* out = new (std::nothrow) char[s.size() + 1];
22+
if(out == nullptr) { return nullptr; }
2023
std::memcpy(out, s.data(), s.size());
2124
out[s.size()] = '\0';
2225
return out;
@@ -37,44 +40,39 @@ inline void plw_string_free(char* s) { delete[] s; }
3740
inline plw_experiment_t plw_experiment_create(int type_value)
3841
{
3942
using namespace phy_engine::phy_lab_wrapper;
40-
try
43+
c_api::detail::clear_error();
44+
auto type = static_cast<experiment_type>(type_value);
45+
auto* ex = new (std::nothrow) experiment(experiment::create(type));
46+
if(ex == nullptr)
4147
{
42-
auto type = static_cast<experiment_type>(type_value);
43-
return new experiment(experiment::create(type));
44-
}
45-
catch (std::exception const& e)
46-
{
47-
c_api::detail::set_error(e.what());
48-
return nullptr;
49-
}
50-
catch (...)
51-
{
52-
c_api::detail::set_error("unknown error");
48+
c_api::detail::set_error("out of memory");
5349
return nullptr;
5450
}
51+
return ex;
5552
}
5653

5754
inline plw_experiment_t plw_experiment_load_from_string(char const* sav_json)
5855
{
5956
using namespace phy_engine::phy_lab_wrapper;
60-
try
57+
c_api::detail::clear_error();
58+
if(sav_json == nullptr)
6159
{
62-
if (sav_json == nullptr)
63-
{
64-
throw std::invalid_argument("sav_json is null");
65-
}
66-
return new experiment(experiment::load_from_string(sav_json));
60+
c_api::detail::set_error("sav_json is null");
61+
return nullptr;
6762
}
68-
catch (std::exception const& e)
63+
auto r = experiment::load_from_string_ec(sav_json);
64+
if(!r)
6965
{
70-
c_api::detail::set_error(e.what());
66+
c_api::detail::set_error(r.st.message);
7167
return nullptr;
7268
}
73-
catch (...)
69+
auto* ex = new (std::nothrow) experiment(std::move(*r.value));
70+
if(ex == nullptr)
7471
{
75-
c_api::detail::set_error("unknown error");
72+
c_api::detail::set_error("out of memory");
7673
return nullptr;
7774
}
75+
return ex;
7876
}
7977

8078
inline void plw_experiment_destroy(plw_experiment_t handle)
@@ -86,25 +84,26 @@ inline void plw_experiment_destroy(plw_experiment_t handle)
8684
inline char* plw_experiment_dump(plw_experiment_t handle, int indent)
8785
{
8886
using namespace phy_engine::phy_lab_wrapper;
89-
try
87+
c_api::detail::clear_error();
88+
auto* ex = static_cast<experiment*>(handle);
89+
if(ex == nullptr)
9090
{
91-
auto* ex = static_cast<experiment*>(handle);
92-
if (ex == nullptr)
93-
{
94-
throw std::invalid_argument("experiment handle is null");
95-
}
96-
return c_api::detail::dup_cstr(ex->dump(indent));
91+
c_api::detail::set_error("experiment handle is null");
92+
return nullptr;
9793
}
98-
catch (std::exception const& e)
94+
auto r = ex->dump_ec(indent);
95+
if(!r)
9996
{
100-
c_api::detail::set_error(e.what());
97+
c_api::detail::set_error(r.st.message);
10198
return nullptr;
10299
}
103-
catch (...)
100+
auto* out = c_api::detail::dup_cstr(*r.value);
101+
if(out == nullptr)
104102
{
105-
c_api::detail::set_error("unknown error");
103+
c_api::detail::set_error("out of memory");
106104
return nullptr;
107105
}
106+
return out;
108107
}
109108

110109
inline char* plw_experiment_add_circuit_element(plw_experiment_t handle,
@@ -115,30 +114,31 @@ inline char* plw_experiment_add_circuit_element(plw_experiment_t handle,
115114
int element_xyz_coords)
116115
{
117116
using namespace phy_engine::phy_lab_wrapper;
118-
try
117+
c_api::detail::clear_error();
118+
auto* ex = static_cast<experiment*>(handle);
119+
if(ex == nullptr)
119120
{
120-
auto* ex = static_cast<experiment*>(handle);
121-
if (ex == nullptr)
122-
{
123-
throw std::invalid_argument("experiment handle is null");
124-
}
125-
if (model_id == nullptr)
126-
{
127-
throw std::invalid_argument("model_id is null");
128-
}
129-
auto id = ex->add_circuit_element(model_id, position{x, y, z}, element_xyz_coords != 0);
130-
return c_api::detail::dup_cstr(id);
121+
c_api::detail::set_error("experiment handle is null");
122+
return nullptr;
123+
}
124+
if(model_id == nullptr)
125+
{
126+
c_api::detail::set_error("model_id is null");
127+
return nullptr;
131128
}
132-
catch (std::exception const& e)
129+
auto id_r = ex->add_circuit_element_ec(model_id, position{x, y, z}, element_xyz_coords != 0);
130+
if(!id_r)
133131
{
134-
c_api::detail::set_error(e.what());
132+
c_api::detail::set_error(id_r.st.message);
135133
return nullptr;
136134
}
137-
catch (...)
135+
auto* out = c_api::detail::dup_cstr(*id_r.value);
136+
if(out == nullptr)
138137
{
139-
c_api::detail::set_error("unknown error");
138+
c_api::detail::set_error("out of memory");
140139
return nullptr;
141140
}
141+
return out;
142142
}
143143

144144
inline int plw_experiment_connect(plw_experiment_t handle,
@@ -149,31 +149,25 @@ inline int plw_experiment_connect(plw_experiment_t handle,
149149
int color_value)
150150
{
151151
using namespace phy_engine::phy_lab_wrapper;
152-
try
152+
c_api::detail::clear_error();
153+
auto* ex = static_cast<experiment*>(handle);
154+
if(ex == nullptr)
153155
{
154-
auto* ex = static_cast<experiment*>(handle);
155-
if (ex == nullptr)
156-
{
157-
throw std::invalid_argument("experiment handle is null");
158-
}
159-
if (src_id == nullptr || dst_id == nullptr)
160-
{
161-
throw std::invalid_argument("src_id/dst_id is null");
162-
}
163-
ex->connect(src_id, src_pin, dst_id, dst_pin, static_cast<wire_color>(color_value));
164-
return 0;
156+
c_api::detail::set_error("experiment handle is null");
157+
return 1;
165158
}
166-
catch (std::exception const& e)
159+
if(src_id == nullptr || dst_id == nullptr)
167160
{
168-
c_api::detail::set_error(e.what());
161+
c_api::detail::set_error("src_id/dst_id is null");
169162
return 1;
170163
}
171-
catch (...)
164+
auto st = ex->connect_ec(src_id, src_pin, dst_id, dst_pin, static_cast<wire_color>(color_value));
165+
if(!st)
172166
{
173-
c_api::detail::set_error("unknown error");
167+
c_api::detail::set_error(st.message);
174168
return 1;
175169
}
170+
return 0;
176171
}
177172

178173
} // extern "C"
179-
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <utility>
5+
6+
namespace phy_engine::phy_lab_wrapper::detail
7+
{
8+
inline thread_local std::string last_error{};
9+
10+
inline void set_last_error(std::string msg) { last_error = std::move(msg); }
11+
inline void clear_last_error() noexcept { last_error.clear(); }
12+
13+
[[nodiscard]] inline char const* last_error_c_str() noexcept { return last_error.c_str(); }
14+
} // namespace phy_engine::phy_lab_wrapper::detail
15+

include/phy_engine/phy_lab_wrapper/layout_locator.h

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct corner_locator
3030
position x_axis{}; // left->right direction (u axis)
3131
position y_axis{}; // bottom->top direction (v axis)
3232

33-
[[nodiscard]] static corner_locator from_experiment(experiment const& ex, corner_markers markers)
33+
[[nodiscard]] static status_or<corner_locator> from_experiment_ec(experiment const& ex, corner_markers markers) noexcept
3434
{
3535
auto find_first_by_model_id = [&](std::string_view model_id) -> std::optional<position> {
3636
for(auto const& e : ex.elements())
@@ -53,10 +53,12 @@ struct corner_locator
5353
auto missing = [&](std::optional<position> const& p, std::string_view name, std::string_view mid) -> std::string {
5454
return p ? std::string{} : (std::string(name) + " (" + std::string(mid) + ") ");
5555
};
56-
throw std::runtime_error("corner_locator: missing marker(s): " + missing(lt, "left_top", markers.left_top_model_id) +
57-
missing(lb, "left_bottom", markers.left_bottom_model_id) +
58-
missing(rt, "right_top", markers.right_top_model_id) +
59-
missing(rb, "right_bottom", markers.right_bottom_model_id));
56+
auto msg = "corner_locator: missing marker(s): " + missing(lt, "left_top", markers.left_top_model_id) +
57+
missing(lb, "left_bottom", markers.left_bottom_model_id) +
58+
missing(rt, "right_top", markers.right_top_model_id) +
59+
missing(rb, "right_bottom", markers.right_bottom_model_id);
60+
::phy_engine::phy_lab_wrapper::detail::set_last_error(msg);
61+
return status{std::errc::invalid_argument, std::move(msg)};
6062
}
6163

6264
corner_locator out{};
@@ -81,11 +83,28 @@ struct corner_locator
8183
return out;
8284
}
8385

86+
[[nodiscard]] static status_or<corner_locator> from_sav_ec(std::filesystem::path const& sav_path, corner_markers markers) noexcept
87+
{
88+
auto ex_r = experiment::load_ec(sav_path);
89+
if(!ex_r) { return ex_r.st; }
90+
return from_experiment_ec(*ex_r.value, markers);
91+
}
92+
93+
#if PHY_ENGINE_ENABLE_EXCEPTIONS
94+
[[nodiscard]] static corner_locator from_experiment(experiment const& ex, corner_markers markers)
95+
{
96+
auto r = from_experiment_ec(ex, markers);
97+
if(!r) { throw std::runtime_error(r.st.message); }
98+
return std::move(*r.value);
99+
}
100+
84101
[[nodiscard]] static corner_locator from_sav(std::filesystem::path const& sav_path, corner_markers markers)
85102
{
86-
auto ex = experiment::load(sav_path);
87-
return from_experiment(ex, markers);
103+
auto r = from_sav_ec(sav_path, markers);
104+
if(!r) { throw std::runtime_error(r.st.message); }
105+
return std::move(*r.value);
88106
}
107+
#endif
89108

90109
// Map a point in the unit square (u,v in [0,1]) into native coordinates.
91110
// u: left->right, v: bottom->top.

0 commit comments

Comments
 (0)