|
| 1 | +// NOTE: this assumes neuronapi.h is on your CPLUS_INCLUDE_PATH |
| 2 | +// Exercises nrn_object_new_nothrow, which constructs an object like |
| 3 | +// nrn_object_new but reports a HOC constructor error through a return code and |
| 4 | +// error buffer instead of letting a C++ exception cross the call boundary. |
| 5 | +#include <array> |
| 6 | +#include <iostream> |
| 7 | +#include "neuronapi.h" |
| 8 | + |
| 9 | +using std::cerr; |
| 10 | +using std::endl; |
| 11 | + |
| 12 | +extern "C" void modl_reg(){/* No modl_reg */}; |
| 13 | + |
| 14 | +static bool check(bool cond, const char* msg) { |
| 15 | + if (!cond) { |
| 16 | + cerr << "FAIL: " << msg << endl; |
| 17 | + } |
| 18 | + return cond; |
| 19 | +} |
| 20 | + |
| 21 | +int main(void) { |
| 22 | + static std::array<const char*, 4> argv = {"object_new_nothrow", "-nogui", "-nopython", nullptr}; |
| 23 | + nrn_init(3, argv.data()); |
| 24 | + |
| 25 | + bool ok = true; |
| 26 | + char err[256]; |
| 27 | + |
| 28 | + // Success path: a Vector(3) constructs, returns 0, and fills *result. |
| 29 | + Object* v = nullptr; |
| 30 | + nrn_double_push(3); |
| 31 | + int rc = nrn_object_new_nothrow(nrn_symbol("Vector"), 1, &v, err, sizeof(err)); |
| 32 | + ok &= check(rc == 0, "successful construction returns 0"); |
| 33 | + ok &= check(v != nullptr, "successful construction sets *result"); |
| 34 | + ok &= check(v != nullptr && nrn_vector_capacity(v) == 3, "constructed the requested Vector(3)"); |
| 35 | + ok &= check(err[0] == '\0', "error buffer stays empty on success"); |
| 36 | + |
| 37 | + // Failure path: a template whose init calls execerror. nrn_object_new would |
| 38 | + // throw; nrn_object_new_nothrow must catch it. |
| 39 | + nrn_hoc_call("begintemplate Boom\nproc init() { execerror(\"boom\", \"\") }\nendtemplate Boom"); |
| 40 | + Object* b = reinterpret_cast<Object*>(0x1); // sentinel; must be nulled |
| 41 | + rc = nrn_object_new_nothrow(nrn_symbol("Boom"), 0, &b, err, sizeof(err)); |
| 42 | + ok &= check(rc != 0, "a failing constructor returns nonzero"); |
| 43 | + ok &= check(b == nullptr, "*result is NULL after a failed construction"); |
| 44 | + ok &= check(err[0] != '\0', "error buffer is populated on failure"); |
| 45 | + |
| 46 | + // A failed construction must not corrupt the stack: a value pushed after it |
| 47 | + // pops back cleanly. |
| 48 | + nrn_double_push(42.0); |
| 49 | + ok &= check(nrn_double_pop() == 42.0, "stack is intact after a failed construction"); |
| 50 | + |
| 51 | + // A NULL error buffer is tolerated on both paths. |
| 52 | + Object* v2 = nullptr; |
| 53 | + rc = nrn_object_new_nothrow(nrn_symbol("Vector"), 0, &v2, nullptr, 0); |
| 54 | + ok &= check(rc == 0 && v2 != nullptr, "NULL error buffer tolerated on success"); |
| 55 | + Object* b2 = nullptr; |
| 56 | + rc = nrn_object_new_nothrow(nrn_symbol("Boom"), 0, &b2, nullptr, 0); |
| 57 | + ok &= check(rc != 0 && b2 == nullptr, "NULL error buffer tolerated on failure"); |
| 58 | + |
| 59 | + return ok ? 0 : 1; |
| 60 | +} |
0 commit comments