Skip to content

Commit d9c5fab

Browse files
authored
neuronapi: add nrn_object_ptr_push for writable objref arguments (#3823)
* neuronapi: add nrn_object_ref_push for writable objref arguments * address review: rename nrn_object_ref_push -> nrn_object_ptr_push Per McDougal's #3823 review: in this API 'ref' means reference counting (nrn_object_ref/unref); a function that pushes a pointer is named 'ptr' (cf. nrn_double_ptr_push). Renames the function and its test, and notes on nrn_object_push that assigning to a non-objref arg is a HOC error. * neuronapi: reword 'cell' to 'slot' in nrn_object_ptr_push docs Per review: 'cell' collides with its NEURON meaning (a neuron). Use 'slot' for the Object* storage location in the docs and the matching source comment.
1 parent a698030 commit d9c5fab

5 files changed

Lines changed: 98 additions & 1 deletion

File tree

docs/capi.rst

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,42 @@ Functions, objects, and the stack
924924
925925
**Usage Pattern:**
926926
927-
Used when passing objects as arguments to functions or methods.
927+
Used when passing objects as arguments to functions or methods. The callee
928+
receives the object by value; if the callee's argument was not declared as
929+
an ``objref`` and it tries to assign to it (``$oN = ...``), HOC raises an
930+
error. To pass an object reference a callee can assign back into, use
931+
:c:func:`nrn_object_ptr_push`.
932+
933+
.. c:function:: void nrn_object_ptr_push(Object** obj_ref)
934+
935+
Push a writable object-reference slot onto the stack.
936+
937+
:param obj_ref: Address of the caller's ``Object*`` slot.
938+
939+
Unlike :c:func:`nrn_object_push`, which pushes an object by value, this
940+
pushes the *slot* holding the object. When the callee assigns to the
941+
matching ``$oN`` argument, the assignment writes back through the slot and
942+
updates ``*obj_ref`` in place. This is the out-parameter form used by the
943+
``h.ref(obj)`` idiom, where a function returns a value by storing it in a
944+
caller-supplied object reference. ("ptr", as in :c:func:`nrn_double_ptr_push`,
945+
is the pushed-pointer naming; the "ref" in :c:func:`nrn_object_ref` is
946+
reference counting.)
947+
948+
**Usage Pattern:**
949+
950+
.. code-block:: c
951+
952+
// proc setit() { $o1 = new Vector(3) }
953+
Object* slot = nullptr;
954+
nrn_object_ptr_push(&slot);
955+
nrn_function_call(nrn_symbol("setit"), 1);
956+
// slot now points to the newly created Vector; unref when done.
957+
nrn_object_unref(slot);
958+
959+
.. seealso::
960+
961+
:c:func:`nrn_object_push`,
962+
:c:func:`nrn_object_unref`
928963

929964
.. c:function:: Object* nrn_object_pop(void)
930965

src/nrniv/neuronapi.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,17 @@ void nrn_object_push(Object* obj) {
377377
hoc_push_object(obj);
378378
}
379379

380+
void nrn_object_ptr_push(Object** obj_ref) {
381+
// Push a writable object-reference slot (the out-parameter form of
382+
// nrn_object_push). When a callee assigns to the corresponding $oN arg,
383+
// hoc assigns through this slot, updating *obj_ref in place. Unlike
384+
// nrn_object_push, which pushes an object by value, this exposes the
385+
// h.ref(obj) idiom (a callee that writes back into the caller's objref).
386+
// Named for the pointer it pushes (cf. nrn_double_ptr_push); the "ref" in
387+
// nrn_object_ref/unref is reference counting, a different concept.
388+
hoc_pushobj(obj_ref);
389+
}
390+
380391
Object* nrn_object_pop(void) {
381392
// NOTE: the returned object should be unref'd when no longer needed
382393
Object** obptr = hoc_objpop();

src/nrniv/neuronapi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ char** nrn_str_pop(void);
9595
void nrn_int_push(int i);
9696
int nrn_int_pop(void);
9797
void nrn_object_push(Object* obj);
98+
void nrn_object_ptr_push(Object** obj_ref);
9899
Object* nrn_object_pop(void);
99100
nrn_stack_types_t nrn_stack_type(void);
100101
char const* nrn_stack_type_name(nrn_stack_types_t id);

test/api/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ foreach(
66
node_index.cpp
77
object_new_nothrow.cpp
88
object_new_wrap.cpp
9+
object_ptr_push.cpp
910
segment_diam.cpp
1011
sections.cpp
1112
symbol_object_string.cpp

test/api/object_ptr_push.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// NOTE: this assumes neuronapi.h is on your CPLUS_INCLUDE_PATH
2+
// Exercises nrn_object_ptr_push, which pushes a writable object-reference cell
3+
// (an Object**) rather than an object by value. This is the out-parameter form
4+
// behind the h.ref(obj) idiom: a callee that assigns to its $oN arg writes back
5+
// through the cell. nrn_object_push (by value) cannot express this.
6+
#include <array>
7+
#include <iostream>
8+
#include "neuronapi.h"
9+
10+
using std::cerr;
11+
using std::endl;
12+
13+
extern "C" void modl_reg(){/* No modl_reg */};
14+
15+
static bool check(bool cond, const char* msg) {
16+
if (!cond) {
17+
cerr << "FAIL: " << msg << endl;
18+
}
19+
return cond;
20+
}
21+
22+
int main(void) {
23+
static std::array<const char*, 4> argv = {"object_ptr_push", "-nogui", "-nopython", nullptr};
24+
nrn_init(3, argv.data());
25+
26+
bool ok = true;
27+
28+
// A proc whose sole job is to write a fresh Vector back into its objref arg.
29+
nrn_hoc_call("proc make3() { $o1 = new Vector(3) }");
30+
// And one that reads the passed object's size into the built-in hoc_ac_.
31+
nrn_hoc_call("proc readsize() { hoc_ac_ = $o1.size() }");
32+
33+
// Write-back: push the address of an empty cell, call make3, and confirm the
34+
// cell was populated. A by-value push could not update `slot` here.
35+
Object* slot = nullptr;
36+
nrn_object_ptr_push(&slot);
37+
nrn_function_call(nrn_symbol("make3"), 1);
38+
ok &= check(slot != nullptr, "objref cell was written back by the callee");
39+
ok &= check(nrn_vector_capacity(slot) == 3, "written-back Vector has capacity 3");
40+
41+
// Read direction: push the now-populated cell and have HOC read $o1.size().
42+
nrn_object_ptr_push(&slot);
43+
nrn_function_call(nrn_symbol("readsize"), 1);
44+
double* ac = nrn_symbol_dataptr(nrn_symbol("hoc_ac_"));
45+
ok &= check(ac != nullptr && *ac == 3.0, "HOC read the pushed object's size through the cell");
46+
47+
nrn_object_unref(slot);
48+
return ok ? 0 : 1;
49+
}

0 commit comments

Comments
 (0)