From e936e267da387334054bb492d3158f45ae60afcc Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 26 Jan 2025 11:20:57 -0600 Subject: [PATCH] [sourcegen] Split accessor from constructor methods --- .../sourcegen/sourcegen/_data/README.md | 9 ++- .../sourcegen/_data/ctfunc_auto.yaml | 5 +- .../sourcegen/sourcegen/_data/ctkin_auto.yaml | 9 ++- .../sourcegen/sourcegen/_data/ctsol_auto.yaml | 4 +- .../sourcegen/clib/_CLibSourceGenerator.py | 3 + .../sourcegen/sourcegen/clib/templates.yaml | 80 +++++++++++-------- test/clib_experimental/test_ctfunc3.cpp | 12 +-- 7 files changed, 68 insertions(+), 54 deletions(-) diff --git a/interfaces/sourcegen/sourcegen/_data/README.md b/interfaces/sourcegen/sourcegen/_data/README.md index bf60224d1..f54f37534 100644 --- a/interfaces/sourcegen/sourcegen/_data/README.md +++ b/interfaces/sourcegen/sourcegen/_data/README.md @@ -23,10 +23,11 @@ based on a recipe, which is subsequently used to scaffold API functions using de *Jinja* templates. The following recipe/CLib function types are differentiated: - `function`: Regular function defined in the `Cantera` namespace. -- `constructor`: A CLib constructor adds new (or existing) C++ objects to CLib storage. - As all objects are handled via smart `shared_ptr<>`, a CLib constructor requires a - C++ utility functions that returning a pointer to a new object or an object that is - not yet added to CLib storage. Constructor names should start with `new`. +- `constructor`: A CLib constructor adds new C++ objects to CLib storage. Constructor + names should start with `new`. As all objects are handled via smart `shared_ptr<>`, + CLib constructors require C++ utility functions that return a pointer to a new + object. Constructors with the name `new` will use the C++ default constructor. +- `accessor`: a CLib accessor adds existing or spawned C++ objects to CLib storage. - `destructor`: A CLib destructor removes a C++ object from CLib. Destructor names should start with `del`. - `getter`: Implements a getter method of a C++ class. diff --git a/interfaces/sourcegen/sourcegen/_data/ctfunc_auto.yaml b/interfaces/sourcegen/sourcegen/_data/ctfunc_auto.yaml index 6b15bd50c..9c2fb9aee 100644 --- a/interfaces/sourcegen/sourcegen/_data/ctfunc_auto.yaml +++ b/interfaces/sourcegen/sourcegen/_data/ctfunc_auto.yaml @@ -27,9 +27,8 @@ recipes: implements: newRatioFunction - name: type - name: eval -- name: newDerivative - what: constructor - implements: Func1::derivative +- name: derivative + what: accessor - name: write - name: del what: destructor diff --git a/interfaces/sourcegen/sourcegen/_data/ctkin_auto.yaml b/interfaces/sourcegen/sourcegen/_data/ctkin_auto.yaml index e9c440bee..2621f38f9 100644 --- a/interfaces/sourcegen/sourcegen/_data/ctkin_auto.yaml +++ b/interfaces/sourcegen/sourcegen/_data/ctkin_auto.yaml @@ -9,15 +9,16 @@ base: Kinetics parents: [] # List of parent classes derived: [InterfaceKinetics] # List of specializations recipes: -- name: kineticsType # previously getType +- name: kineticsType # previously: getType - name: nReactions - name: reaction # new uses: nReactions - what: constructor # registers object in CLib storage + what: accessor - name: nPhases - name: phase uses: nPhases - what: constructor # registers object in CLib storage + what: accessor +- name: reactionPhase # new - name: phaseIndex - name: nTotalSpecies # previously: nSpecies - name: reactantStoichCoeff @@ -45,7 +46,7 @@ recipes: - name: getDeltaSSGibbs # previously: part of getDelta - name: getDeltaSSEntropy # previously: part of getDelta # - name: getSourceTerms # <--- used by MATLAB interface for "massProdRate" -# - name: start # <--- appears to be unused +# - name: start # <--- unused except for FORTRAN API - name: del what: noop brief: Destructor; required by some APIs although object is managed by Solution. diff --git a/interfaces/sourcegen/sourcegen/_data/ctsol_auto.yaml b/interfaces/sourcegen/sourcegen/_data/ctsol_auto.yaml index 7a92d2253..556b3307a 100644 --- a/interfaces/sourcegen/sourcegen/_data/ctsol_auto.yaml +++ b/interfaces/sourcegen/sourcegen/_data/ctsol_auto.yaml @@ -26,12 +26,12 @@ recipes: - name: transportModel - name: setTransportModel uses: [transport] - what: constructor + what: accessor - name: nAdjacent - name: adjacent implements: Solution::adjacent(size_t) uses: [nAdjacent, thermo, kinetics, transport] - what: constructor # registers object in CLib storage + what: accessor - name: adjacentName - name: source - name: cabinetSize diff --git a/interfaces/sourcegen/sourcegen/clib/_CLibSourceGenerator.py b/interfaces/sourcegen/sourcegen/clib/_CLibSourceGenerator.py index e01e9eead..ba5b50d3b 100644 --- a/interfaces/sourcegen/sourcegen/clib/_CLibSourceGenerator.py +++ b/interfaces/sourcegen/sourcegen/clib/_CLibSourceGenerator.py @@ -336,6 +336,9 @@ class CLibSourceGenerator(SourceGenerator): elif recipe.what == "constructor": template = loader.from_string(self._templates["clib-constructor"]) + elif recipe.what == "accessor": + template = loader.from_string(self._templates["clib-accessor"]) + elif recipe.what == "destructor": template = loader.from_string(self._templates["clib-destructor"]) diff --git a/interfaces/sourcegen/sourcegen/clib/templates.yaml b/interfaces/sourcegen/sourcegen/clib/templates.yaml index 3be2be0aa..2f02a1db7 100644 --- a/interfaces/sourcegen/sourcegen/clib/templates.yaml +++ b/interfaces/sourcegen/sourcegen/clib/templates.yaml @@ -134,30 +134,62 @@ clib-function: |- } clib-constructor: |- - ## CLib constructor template: - ## may either instantiate new object or expose existing C++ object to CLib + ## CLib constructor template: instantiates new object // constructor: {{ cxx_implements }} try { {% for line in lines %} ## add lines used for CLib/C++ variable crosswalk {{ line }} {% endfor %} - {% if handle and checks %} - ## constructor accesses existing object and uses index checker (see: sol3_adjacent) - if ({{ c_args[1] }} < 0 || {{ c_args[1] }} >= {{ base }}Cabinet::at({{ handle }})->{{ checks[0] }}()) { - throw IndexError("{{ c_func }}", "", {{ c_args[1] }}, {{ base }}Cabinet::at({{ handle }})->{{ checks[0] }}() - 1); - } - {% endif %}{# handle and checks #} {% if shared %} ## instantiated object manages associated objects (see: sol3_newSolution) ## storage of associated objects requires id (handle) of new object - {% if handle %} - ## constructor exposes existing C++ object to CLib - ## the accessed C++ object may have a different base + auto obj = {{ cxx_name }}({{ ', '.join(cxx_args) }}); + int id = {{ base }}Cabinet::add(obj); + {% for typ, getter in shared %} + ## add associated objects (see: sol3_newSolution, sol3_adjacent) + if (obj->{{ getter }}()) { + {{ typ }}Cabinet::add(obj->{{ getter }}(), id); + } + {% endfor %} + return id; + {% else %}{# not shared #} + ## instantiated object has no associated objects; no id is needed + return {{ base }}Cabinet::add({{ cxx_name }}({{ ', '.join(cxx_args) }})); + {% endif %}{# shared #} + } catch (...) { + return handleAllExceptions({{ error[0] }}, {{ error[1] }}); + } + +clib-accessor: |- + ## CLib accessor template: exposes existing C++ object to CLib + // accessor: {{ cxx_implements }} + try { + {% for line in lines %} + ## add lines used for CLib/C++ variable crosswalk + {{ line }} + {% endfor %} + {% if checks %} + ## accessor uses index checker (see: sol3_adjacent) + if ({{ c_args[1] }} < 0 || {{ c_args[1] }} >= {{ base }}Cabinet::at({{ handle }})->{{ checks[0] }}()) { + throw IndexError("{{ c_func }}", "", {{ c_args[1] }}, {{ base }}Cabinet::at({{ handle }})->{{ checks[0] }}() - 1); + } + {% endif %}{# checks #} + {% if shared %} + ## accessed object manages associated objects (see: sol3_newSolution) + ## storage of associated objects requires id (handle) of new object {% if cxx_rbase %} - ## method returns new object + ## returned and accessed C++ objects may have a different bases auto obj = {{ cxx_base }}Cabinet::at({{ handle }})->{{ cxx_name }}({{ ', '.join(cxx_args) }}); int id = {{ cxx_rbase }}Cabinet::add(obj); + ## return handle of newly created / accessed object + {% for typ, getter in shared %} + ## add associated objects (see: sol3_newSolution, sol3_adjacent) + if (obj->{{ getter }}()) { + {{ typ }}Cabinet::add(obj->{{ getter }}(), id); + } + {% endfor %} + return id; {% else %}{# not cxx_rbase #} ## method modifies associated objects (see: sol3_setTransportModel) auto obj = {{ cxx_base }}Cabinet::at({{ handle }}); @@ -169,22 +201,6 @@ clib-constructor: |- } {% endfor %} obj->{{ cxx_name }}({{ ', '.join(cxx_args) }}); - {% endif %}{# cxx_rbase #} - {% else %}{# not handle #} - ## constructor creates new object - auto obj = {{ cxx_name }}({{ ', '.join(cxx_args) }}); - int id = {{ base }}Cabinet::add(obj); - {% endif %}{# handle #} - {% if cxx_rbase %} - ## return handle of newly created / accessed object - {% for typ, getter in shared %} - ## add associated objects (see: sol3_newSolution, sol3_adjacent) - if (obj->{{ getter }}()) { - {{ typ }}Cabinet::add(obj->{{ getter }}(), id); - } - {% endfor %} - return id; - {% else %}{# not cxx_rbase #} ## return handle to modified associated object (see: sol3_setTransportModel) {% for typ, getter in shared %} ## shared should have single entry @@ -193,14 +209,8 @@ clib-constructor: |- {% endif %}{# cxx_rbase #} {% else %}{# not shared #} ## instantiated object has no associated objects; no id is needed - {% if handle %} - ## constructor exposes existing C++ object to CLib - ## the accessed C++ object may have a different base + ## returned and accessed C++ objects may have a different bases return {{ cxx_rbase }}Cabinet::add({{ base }}Cabinet::at({{ handle }})->{{ cxx_name }}({{ ', '.join(cxx_args) }})); - {% else %}{# not handle #} - ## constructor creates new object - return {{ base }}Cabinet::add({{ cxx_name }}({{ ', '.join(cxx_args) }})); - {% endif %}{# handle #} {% endif %}{# shared #} } catch (...) { return handleAllExceptions({{ error[0] }}, {{ error[1] }}); diff --git a/test/clib_experimental/test_ctfunc3.cpp b/test/clib_experimental/test_ctfunc3.cpp index f46cd5630..95a311169 100644 --- a/test/clib_experimental/test_ctfunc3.cpp +++ b/test/clib_experimental/test_ctfunc3.cpp @@ -23,7 +23,7 @@ TEST(ctfunc3, sin) ASSERT_GE(fcn, 0); EXPECT_DOUBLE_EQ(func13_eval(fcn, 0.5), sin(omega * 0.5)); - int dfcn = func13_newDerivative(fcn); + int dfcn = func13_derivative(fcn); EXPECT_DOUBLE_EQ(func13_eval(dfcn, 0.5), omega * cos(omega * 0.5)); int buflen = func13_write(fcn, "x", 0, 0); @@ -40,7 +40,7 @@ TEST(ctfunc3, cos) ASSERT_GE(fcn, 0); EXPECT_DOUBLE_EQ(func13_eval(fcn, 0.5), cos(omega * 0.5)); - int dfcn = func13_newDerivative(fcn); + int dfcn = func13_derivative(fcn); EXPECT_DOUBLE_EQ(func13_eval(dfcn, 0.5), -omega * sin(omega * 0.5)); } @@ -51,7 +51,7 @@ TEST(ctfunc3, exp) ASSERT_GE(fcn, 0); EXPECT_DOUBLE_EQ(func13_eval(fcn, 0.5), exp(omega * 0.5)); - int dfcn = func13_newDerivative(fcn); + int dfcn = func13_derivative(fcn); EXPECT_DOUBLE_EQ(func13_eval(dfcn, 0.5), omega * exp(omega * 0.5)); } @@ -62,7 +62,7 @@ TEST(ctfunc3, log) ASSERT_GE(fcn, 0); EXPECT_DOUBLE_EQ(func13_eval(fcn, 1. / omega), 0.); - int dfcn = func13_newDerivative(fcn); + int dfcn = func13_derivative(fcn); EXPECT_DOUBLE_EQ(func13_eval(dfcn, .5), omega / .5); } @@ -73,7 +73,7 @@ TEST(ctfunc3, pow) ASSERT_GE(fcn, 0); EXPECT_DOUBLE_EQ(func13_eval(fcn, 0.5), pow(0.5, exp)); - int dfcn = func13_newDerivative(fcn); + int dfcn = func13_derivative(fcn); EXPECT_DOUBLE_EQ(func13_eval(dfcn, 0.5), exp * pow(0.5, exp - 1)); } @@ -84,7 +84,7 @@ TEST(ctfunc3, constant) ASSERT_GE(fcn, 0); EXPECT_DOUBLE_EQ(func13_eval(fcn, 0.5), a); - int dfcn = func13_newDerivative(fcn); + int dfcn = func13_derivative(fcn); EXPECT_DOUBLE_EQ(func13_eval(dfcn, .5), 0.); }