Skip to content

Commit 093402d

Browse files
committed
neuronapi: add top-level objref/strdef get and set accessors
Adds nrn_symbol_object_get/set and nrn_symbol_str_get/set. A top-level objref or strdef stores its Object*/char* in the top-level object-data array (hoc_top_level_data[sym->u.oboff].pobj/.ppstr), not at sym->u.pval, so nrn_symbol_dataptr cannot return it. These read and write that storage: object_set follows HOC's assignment refcounting (unref old, ref new; NULL clears), and str_set copies via hoc_assign_str. Non-matching symbol types return NULL / nonzero rather than crashing.
1 parent 61cb128 commit 093402d

5 files changed

Lines changed: 179 additions & 0 deletions

File tree

docs/capi.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,57 @@ Functions, objects, and the stack
750750
Provides direct access to variable data for efficient reading/writing.
751751
e.g., use this for getting/setting the value of ``t`` (time).
752752
753+
.. c:function:: Object* nrn_symbol_object_get(const Symbol* sym)
754+
755+
Get the object bound to a top-level ``objref``.
756+
757+
:param sym: Symbol for a top-level ``objref``.
758+
:returns: The bound object, or ``NULL`` if the objref is nil or ``sym`` is
759+
not an objref.
760+
761+
The object is returned *borrowed* -- its reference count is not
762+
incremented. Call :c:func:`nrn_object_ref` to retain it beyond the next
763+
assignment to the objref. Complements :c:func:`nrn_symbol_dataptr`, which
764+
returns ``NULL`` for an objref because it is not a ``double*``.
765+
766+
.. c:function:: int nrn_symbol_object_set(Symbol* sym, Object* obj)
767+
768+
Bind an object to a top-level ``objref``.
769+
770+
:param sym: Symbol for a top-level ``objref``.
771+
:param obj: The object to bind, or ``NULL`` to make the objref nil.
772+
:returns: 0 on success, nonzero if ``sym`` is not an objref.
773+
774+
Follows HOC's assignment reference-counting: the previously bound object is
775+
released and the new one retained.
776+
777+
.. c:function:: const char* nrn_symbol_str_get(const Symbol* sym)
778+
779+
Get the string held by a top-level ``strdef``.
780+
781+
:param sym: Symbol for a top-level ``strdef``.
782+
:returns: The string, or ``NULL`` if ``sym`` is not a strdef.
783+
784+
.. c:function:: int nrn_symbol_str_set(Symbol* sym, const char* value)
785+
786+
Set the string held by a top-level ``strdef``.
787+
788+
:param sym: Symbol for a top-level ``strdef``.
789+
:param value: The string to copy in.
790+
:returns: 0 on success, nonzero if ``sym`` is not a strdef.
791+
792+
The value is copied into the strdef's storage (the previous string is
793+
freed).
794+
795+
**Python Equivalent:**
796+
797+
.. code-block:: python
798+
799+
n.s = "cell" # nrn_symbol_str_set
800+
name = n.s # nrn_symbol_str_get
801+
n.obj = vec # nrn_symbol_object_set
802+
bound = n.obj # nrn_symbol_object_get
803+
753804
.. c:function:: bool nrn_symbol_is_array(const Symbol* sym)
754805
755806
Check if a symbol represents an array.

src/nrniv/neuronapi.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,53 @@ double* nrn_symbol_dataptr(const Symbol* sym) {
273273
return sym->u.pval;
274274
}
275275

276+
Object* nrn_symbol_object_get(const Symbol* sym) {
277+
// A top-level objref (`objref o`) stores its Object* in the top-level
278+
// object-data array, not at sym->u.pval. Returns the bound object, or NULL
279+
// if the objref is nil or `sym` is not an objref. The object is returned
280+
// borrowed (its reference count is not incremented); call nrn_object_ref to
281+
// retain it past the next assignment to this objref.
282+
if (!sym || sym->type != OBJECTVAR) {
283+
return nullptr;
284+
}
285+
return hoc_top_level_data[sym->u.oboff].pobj[0];
286+
}
287+
288+
int nrn_symbol_object_set(Symbol* sym, Object* obj) {
289+
// Bind `obj` to a top-level objref, following HOC's assignment refcount
290+
// rules: release the previously bound object and retain the new one. A NULL
291+
// obj clears the objref (makes it nil). Returns nonzero if `sym` is not an
292+
// objref.
293+
if (!sym || sym->type != OBJECTVAR) {
294+
return 1;
295+
}
296+
Object** cell = hoc_top_level_data[sym->u.oboff].pobj;
297+
hoc_dec_refcount(cell); // unref the old content and NULL the cell
298+
*cell = obj;
299+
hoc_obj_ref(obj); // NULL-safe
300+
return 0;
301+
}
302+
303+
const char* nrn_symbol_str_get(const Symbol* sym) {
304+
// A top-level strdef (`strdef s`) stores its char* in the top-level
305+
// object-data array. Returns the string, or NULL if `sym` is not a strdef.
306+
if (!sym || sym->type != STRING) {
307+
return nullptr;
308+
}
309+
return hoc_top_level_data[sym->u.oboff].ppstr[0];
310+
}
311+
312+
int nrn_symbol_str_set(Symbol* sym, const char* value) {
313+
// Copy `value` into a top-level strdef's storage (freeing the previous
314+
// string), via the same helper HOC string assignment uses. Returns nonzero
315+
// if `sym` is not a strdef.
316+
if (!sym || sym->type != STRING) {
317+
return 1;
318+
}
319+
hoc_assign_str(hoc_top_level_data[sym->u.oboff].ppstr, value);
320+
return 0;
321+
}
322+
276323
bool nrn_symbol_is_array(const Symbol* sym) {
277324
return sym->arayinfo != nullptr;
278325
}

src/nrniv/neuronapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ void nrn_symbol_push(Symbol* sym);
8181
int nrn_symbol_type(const Symbol* sym);
8282
int nrn_symbol_subtype(const Symbol* sym);
8383
double* nrn_symbol_dataptr(const Symbol* sym);
84+
Object* nrn_symbol_object_get(const Symbol* sym);
85+
int nrn_symbol_object_set(Symbol* sym, Object* obj);
86+
const char* nrn_symbol_str_get(const Symbol* sym);
87+
int nrn_symbol_str_set(Symbol* sym, const char* value);
8488
bool nrn_symbol_is_array(const Symbol* sym);
8589
void nrn_double_push(double val);
8690
double nrn_double_pop(void);

test/api/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ foreach(
77
object_new_wrap.cpp
88
segment_diam.cpp
99
sections.cpp
10+
symbol_object_string.cpp
1011
vclamp.cpp)
1112
string(REPLACE "." "_" api_test_name "${api_test_file}")
1213
add_executable(${api_test_name} ${api_test_file})

test/api/symbol_object_string.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// NOTE: this assumes neuronapi.h is on your CPLUS_INCLUDE_PATH
2+
// Exercises the top-level objref and strdef accessors: nrn_symbol_object_get/set
3+
// and nrn_symbol_str_get/set. These read and write the Object* / char* that a
4+
// top-level `objref`/`strdef` stores in the top-level object-data array, which
5+
// nrn_symbol_dataptr cannot return (it is not a double*).
6+
#include <array>
7+
#include <cstring>
8+
#include <iostream>
9+
#include "neuronapi.h"
10+
11+
using std::cerr;
12+
using std::endl;
13+
14+
extern "C" void modl_reg(){/* No modl_reg */};
15+
16+
static bool check(bool cond, const char* msg) {
17+
if (!cond) {
18+
cerr << "FAIL: " << msg << endl;
19+
}
20+
return cond;
21+
}
22+
23+
int main(void) {
24+
static std::array<const char*, 4> argv = {"symbol_object_string",
25+
"-nogui",
26+
"-nopython",
27+
nullptr};
28+
nrn_init(3, argv.data());
29+
30+
bool ok = true;
31+
32+
// --- strdef get/set ---
33+
nrn_hoc_call("strdef s");
34+
nrn_hoc_call("s = \"hello\"");
35+
Symbol* s = nrn_symbol("s");
36+
ok &= check(s != nullptr, "strdef symbol resolves");
37+
const char* sval = nrn_symbol_str_get(s);
38+
ok &= check(sval != nullptr && std::strcmp(sval, "hello") == 0,
39+
"str_get reads the strdef value");
40+
41+
ok &= check(nrn_symbol_str_set(s, "world") == 0, "str_set succeeds on a strdef");
42+
ok &= check(std::strcmp(nrn_symbol_str_get(s), "world") == 0, "str_set updated the value");
43+
// Confirm HOC sees the written value too (aliases the same storage).
44+
nrn_hoc_call("hoc_ac_ = strcmp(s, \"world\")");
45+
ok &= check(*nrn_symbol_dataptr(nrn_symbol("hoc_ac_")) == 0.0, "HOC reads the str_set value");
46+
47+
// --- objref get/set ---
48+
nrn_hoc_call("objref o");
49+
nrn_hoc_call("o = new Vector(3)");
50+
Symbol* o = nrn_symbol("o");
51+
ok &= check(o != nullptr, "objref symbol resolves");
52+
Object* vec = nrn_symbol_object_get(o);
53+
ok &= check(vec != nullptr, "object_get reads the bound object");
54+
ok &= check(nrn_vector_capacity(vec) == 3, "the bound object is the Vector(3)");
55+
56+
// Rebind the objref to a different Vector via object_set.
57+
Object* vec5 = nrn_object_new(nrn_symbol("Vector"), 0); // empty Vector
58+
nrn_object_ref(vec5);
59+
ok &= check(nrn_symbol_object_set(o, vec5) == 0, "object_set succeeds on an objref");
60+
ok &= check(nrn_symbol_object_get(o) == vec5, "object_set rebound the objref");
61+
// HOC sees the new binding.
62+
nrn_hoc_call("hoc_ac_ = o.size()");
63+
ok &= check(*nrn_symbol_dataptr(nrn_symbol("hoc_ac_")) == 0.0, "HOC sees the rebound object");
64+
65+
// Clearing to nil.
66+
ok &= check(nrn_symbol_object_set(o, nullptr) == 0, "object_set(NULL) clears the objref");
67+
ok &= check(nrn_symbol_object_get(o) == nullptr, "objref is nil after clear");
68+
69+
// --- type mismatches return NULL / nonzero, not a crash ---
70+
ok &= check(nrn_symbol_object_get(s) == nullptr, "object_get on a strdef returns NULL");
71+
ok &= check(nrn_symbol_str_get(o) == nullptr, "str_get on an objref returns NULL");
72+
ok &= check(nrn_symbol_object_set(s, vec5) != 0, "object_set on a strdef is rejected");
73+
ok &= check(nrn_symbol_str_set(o, "x") != 0, "str_set on an objref is rejected");
74+
75+
return ok ? 0 : 1;
76+
}

0 commit comments

Comments
 (0)