11// NOTE: this assumes neuronapi.h is on your CPLUS_INCLUDE_PATH
2- // Exercises nrn_setpointer, which wires an NMODL POINTER range variable to a
3- // source variable's storage (the Python-free form of the HOC `setpointer`
4- // statement / nrn_pointer_assign). The ptrtest density mechanism copies its
5- // POINTER `src` into the range variable `out` in INITIAL, so after wiring
6- // src -> a source double and calling finitialize, reading out confirms the
7- // POINTER now aliases the source. modl_reg registers ptrtest (its generated
8- // _ptrtest_reg is compiled into this test), the way nrniv registers built-ins.
2+ // Exercises nrn_setpointer_pop, which wires an NMODL POINTER range variable to
3+ // the source pointer the caller has pushed onto the stack (the Python-free form
4+ // of the HOC `setpointer` statement / nrn_pointer_assign). Pushing the source
5+ // rather than naming it reuses every existing way of obtaining a pointer. The
6+ // ptrtest density mechanism copies its POINTER `src` into the range variable
7+ // `out` in INITIAL, so after wiring src -> a source double and calling
8+ // finitialize, reading out confirms the POINTER now aliases the source.
9+ // modl_reg registers ptrtest (its generated _ptrtest_reg is compiled into this
10+ // test), the way nrniv registers built-ins.
911#include < array>
1012#include < cstring>
1113#include < iostream>
@@ -58,10 +60,12 @@ int main(void) {
5860 ok &= check (src_sym != nullptr && out_sym != nullptr && v_sym != nullptr ,
5961 " ptrtest range-variable symbols resolve" );
6062
61- // Wire dend(0.5).ptrtest.src -> soma(0.5).v
63+ // Wire dend(0.5).ptrtest.src -> soma(0.5).v. Push the source pointer, then
64+ // let setpointer_pop consume it and assign it to the POINTER's dparam slot.
6265 char err[256 ];
63- int rc = nrn_setpointer (src_sym, dend, 0.5 , v_sym, soma, 0.5 , err, sizeof (err));
64- ok &= check (rc == 0 , " nrn_setpointer succeeds for a valid POINTER wiring" );
66+ nrn_rangevar_push (v_sym, soma, 0.5 );
67+ int rc = nrn_setpointer_pop (src_sym, dend, 0.5 , err, sizeof (err));
68+ ok &= check (rc == 0 , " nrn_setpointer_pop succeeds for a valid POINTER wiring" );
6569 if (rc != 0 ) {
6670 cerr << " error_msg: " << err << endl;
6771 }
@@ -76,15 +80,25 @@ int main(void) {
7680 " POINTER reads the wired source's value after finitialize" );
7781
7882 // Error path 1: a non-POINTER target (out is a plain RANGE var) is rejected
79- // with a nonzero return and a message, not a crash.
80- int rc_bad = nrn_setpointer (out_sym, dend, 0.5 , v_sym, soma, 0.5 , err, sizeof (err));
83+ // with a nonzero return and a message, not a crash. The pushed source is
84+ // still consumed (popped) before the validation fails, so the stack stays
85+ // balanced.
86+ nrn_rangevar_push (v_sym, soma, 0.5 );
87+ int rc_bad = nrn_setpointer_pop (out_sym, dend, 0.5 , err, sizeof (err));
8188 ok &= check (rc_bad != 0 , " non-POINTER target is rejected" );
8289 ok &= check (err[0 ] != ' \0 ' , " rejection fills the error message" );
8390
8491 // Error path 2: the POINTER's mechanism is absent at the target segment
8592 // (soma has no ptrtest). Rejected, not a crash.
86- int rc_absent = nrn_setpointer (src_sym, soma, 0.5 , v_sym, soma, 0.5 , err, sizeof (err));
93+ nrn_rangevar_push (v_sym, soma, 0.5 );
94+ int rc_absent = nrn_setpointer_pop (src_sym, soma, 0.5 , err, sizeof (err));
8795 ok &= check (rc_absent != 0 , " POINTER target on a segment without the mechanism is rejected" );
8896
97+ // Stack hygiene: every call above (success and error paths) consumed
98+ // exactly the one source it was given. Push a sentinel and confirm it comes
99+ // straight back, proving nothing was left on or over-popped from the stack.
100+ nrn_double_push (17.0 );
101+ ok &= eq (nrn_double_pop (), 17.0 , " the stack is balanced after the setpointer_pop calls" );
102+
89103 return ok ? 0 : 1 ;
90104}
0 commit comments