From 1c1d7cf526df34c919d30ca607e30e3ab5e8260a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Zim=C3=A1nyi?= Date: Thu, 3 Sep 2026 03:11:20 +0200 Subject: [PATCH] Answer a value MEOS writes through a pointer to a pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some accessors answer a value that is itself a MEOS value: `set_value_n` on a set of geometries writes a `GSERIALIZED **`, `ttext_value_at_timestamptz` a `text **`, and their kin the same for a circular buffer, a network point, a pose and a jsonb. The buffer holds the ADDRESS of the value rather than the value, so the reader takes that address out of it and wraps it in the class the model gives the type — the same wrap a return of that type gets. 19 accessors reach the object layer that way, so `GeomSet.ValueN` answers a `Geo` and `TText.ValueAtTimestamptz` a `Text`, each null where MEOS says the value does not exist. 1190 methods against 1171, 105 deferred against 124. Two tests read both answers: a geometry set answers the geometry at an index and null past the end, and a temporal text answers its value inside its span and null outside. --- MEOS.NET.Tests/WrappedOutParameterTests.cs | 39 ++++++++++++++++++++++ MEOS.NET/Types/CbufferSet.g.cs | 18 ++++++++++ MEOS.NET/Types/GeomSet.g.cs | 18 ++++++++++ MEOS.NET/Types/NpointSet.g.cs | 18 ++++++++++ MEOS.NET/Types/PoseSet.g.cs | 18 ++++++++++ MEOS.NET/Types/TCbuffer.g.cs | 36 ++++++++++++++++++++ MEOS.NET/Types/TGeo.g.cs | 36 ++++++++++++++++++++ MEOS.NET/Types/TJsonb.g.cs | 36 ++++++++++++++++++++ MEOS.NET/Types/TNpoint.g.cs | 36 ++++++++++++++++++++ MEOS.NET/Types/TPoint.g.cs | 18 ++++++++++ MEOS.NET/Types/TPose.g.cs | 36 ++++++++++++++++++++ MEOS.NET/Types/TRGeometry.g.cs | 18 ++++++++++ MEOS.NET/Types/TText.g.cs | 36 ++++++++++++++++++++ MEOS.NET/Types/TextSet.g.cs | 18 ++++++++++ tools/objectgen.py | 15 ++++++++- 15 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 MEOS.NET.Tests/WrappedOutParameterTests.cs diff --git a/MEOS.NET.Tests/WrappedOutParameterTests.cs b/MEOS.NET.Tests/WrappedOutParameterTests.cs new file mode 100644 index 0000000..530e625 --- /dev/null +++ b/MEOS.NET.Tests/WrappedOutParameterTests.cs @@ -0,0 +1,39 @@ +using MEOS.NET.Types; + +namespace MEOS.NET.Tests +{ + /// + /// Some values MEOS writes through a pointer to a pointer, saying whether the + /// value exists at all: the method answers the value as its own class, or + /// null. + /// + [TestClass] + public class WrappedOutParameterTests : MeosTest + { + [TestMethod] + public void AGeometrySetAnswersTheGeometryAtAnIndex() + { + GeomSet set = (GeomSet)GeomSet.In("{POINT(1 1), POINT(2 2)}")!; + + Geo? first = set.ValueN(1); + + Assert.IsNotNull(first); + Assert.AreEqual("POINT(1 1)", first!.AsText(6)); + Assert.IsNull(set.ValueN(3)); + } + + [TestMethod] + public void ATemporalTextAnswersItsValueAtAMoment() + { + TText temp = (TText)TText.In("[AA@2024-12-06, AA@2024-12-08]")!; + + Text? inside = temp.ValueAtTimestamptz( + new DateTime(2024, 12, 7, 0, 0, 0, DateTimeKind.Utc), true); + + Assert.IsNotNull(inside); + Assert.AreEqual("AA", inside!.ToString()); + Assert.IsNull(temp.ValueAtTimestamptz( + new DateTime(2024, 12, 9, 0, 0, 0, DateTimeKind.Utc), true)); + } + } +} diff --git a/MEOS.NET/Types/CbufferSet.g.cs b/MEOS.NET/Types/CbufferSet.g.cs index 49d3121..2b6da98 100644 --- a/MEOS.NET/Types/CbufferSet.g.cs +++ b/MEOS.NET/Types/CbufferSet.g.cs @@ -27,6 +27,24 @@ public string Out(int maxdd) public Cbuffer? StartValue() => MEOSFactory.WrapCbuffer(Meos.CbuffersetStartValue(this.Ptr)); + public Cbuffer? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.CbuffersetValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapCbuffer(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Cbuffer?[] Values() => MEOSFactory.WrapCbufferArray(Meos.CbuffersetValues(this.Ptr)); diff --git a/MEOS.NET/Types/GeomSet.g.cs b/MEOS.NET/Types/GeomSet.g.cs index ea57dc5..45b2c0a 100644 --- a/MEOS.NET/Types/GeomSet.g.cs +++ b/MEOS.NET/Types/GeomSet.g.cs @@ -20,6 +20,24 @@ internal GeomSet(IntPtr ptr) : base(ptr) { } public Geo? StartValue() => MEOSFactory.WrapGeo(Meos.GeosetStartValue(this.Ptr)); + public Geo? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.GeosetValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapGeo(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Geo?[] Values() => MEOSFactory.WrapGeoArray(Meos.GeosetValues(this.Ptr)); diff --git a/MEOS.NET/Types/NpointSet.g.cs b/MEOS.NET/Types/NpointSet.g.cs index f37ede9..21e8369 100644 --- a/MEOS.NET/Types/NpointSet.g.cs +++ b/MEOS.NET/Types/NpointSet.g.cs @@ -30,6 +30,24 @@ public string Out(int maxdd) public Npoint? StartValue() => MEOSFactory.WrapNpoint(Meos.NpointsetStartValue(this.Ptr)); + public Npoint? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.NpointsetValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapNpoint(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Npoint?[] Values() => MEOSFactory.WrapNpointArray(Meos.NpointsetValues(this.Ptr)); diff --git a/MEOS.NET/Types/PoseSet.g.cs b/MEOS.NET/Types/PoseSet.g.cs index b906a0f..9d2028f 100644 --- a/MEOS.NET/Types/PoseSet.g.cs +++ b/MEOS.NET/Types/PoseSet.g.cs @@ -27,6 +27,24 @@ public string Out(int maxdd) public Pose? StartValue() => MEOSFactory.WrapPose(Meos.PosesetStartValue(this.Ptr)); + public Pose? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.PosesetValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapPose(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Pose?[] Values() => MEOSFactory.WrapPoseArray(Meos.PosesetValues(this.Ptr)); diff --git a/MEOS.NET/Types/TCbuffer.g.cs b/MEOS.NET/Types/TCbuffer.g.cs index 915db03..6fd0139 100644 --- a/MEOS.NET/Types/TCbuffer.g.cs +++ b/MEOS.NET/Types/TCbuffer.g.cs @@ -62,6 +62,42 @@ internal TCbuffer(IntPtr ptr) : base(ptr) { } public Geo? TraversedArea(bool unary_union) => MEOSFactory.WrapGeo(Meos.TcbufferTraversedArea(this.Ptr, unary_union)); + public Cbuffer? ValueAtTimestamptz(DateTime t, bool strict) + { + IntPtr _value = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TcbufferValueAtTimestamptz(this.Ptr, MEOSConvert.ToTimestampTz(t), strict, _value)) + { + return null; + } + + return MEOSFactory.WrapCbuffer(Marshal.ReadIntPtr(_value)); + } + finally + { + Marshal.FreeHGlobal(_value); + } + } + + public Cbuffer? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TcbufferValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapCbuffer(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Cbuffer?[] Values() => MEOSFactory.WrapCbufferArray(Meos.TcbufferValues(this.Ptr)); diff --git a/MEOS.NET/Types/TGeo.g.cs b/MEOS.NET/Types/TGeo.g.cs index 249eda8..3de465c 100644 --- a/MEOS.NET/Types/TGeo.g.cs +++ b/MEOS.NET/Types/TGeo.g.cs @@ -90,6 +90,42 @@ internal TGeo(IntPtr ptr) : base(ptr) { } public Geo? TraversedArea(bool unary_union) => MEOSFactory.WrapGeo(Meos.TgeoTraversedArea(this.Ptr, unary_union)); + public Geo? ValueAtTimestamptz(DateTime t, bool strict) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TgeoValueAtTimestamptz(this.Ptr, MEOSConvert.ToTimestampTz(t), strict, _result)) + { + return null; + } + + return MEOSFactory.WrapGeo(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + + public Geo? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TgeoValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapGeo(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Geo?[] Values() => MEOSFactory.WrapGeoArray(Meos.TgeoValues(this.Ptr)); diff --git a/MEOS.NET/Types/TJsonb.g.cs b/MEOS.NET/Types/TJsonb.g.cs index bbe0c65..ede2b4b 100644 --- a/MEOS.NET/Types/TJsonb.g.cs +++ b/MEOS.NET/Types/TJsonb.g.cs @@ -168,6 +168,42 @@ public string Out() public Temporal? ToTtext() => MEOSFactory.WrapTemporal(Meos.TjsonbToTtext(this.Ptr)); + public Jsonb? ValueAtTimestamptz(DateTime t, bool strict) + { + IntPtr _value = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TjsonbValueAtTimestamptz(this.Ptr, MEOSConvert.ToTimestampTz(t), strict, _value)) + { + return null; + } + + return MEOSFactory.WrapJsonb(Marshal.ReadIntPtr(_value)); + } + finally + { + Marshal.FreeHGlobal(_value); + } + } + + public Jsonb? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TjsonbValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapJsonb(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Jsonb?[] Values() => MEOSFactory.WrapJsonbArray(Meos.TjsonbValues(this.Ptr)); diff --git a/MEOS.NET/Types/TNpoint.g.cs b/MEOS.NET/Types/TNpoint.g.cs index 205736c..d47027a 100644 --- a/MEOS.NET/Types/TNpoint.g.cs +++ b/MEOS.NET/Types/TNpoint.g.cs @@ -78,6 +78,42 @@ public long Route() public Geo? Twcentroid() => MEOSFactory.WrapGeo(Meos.TnpointTwcentroid(this.Ptr)); + public Npoint? ValueAtTimestamptz(DateTime t, bool strict) + { + IntPtr _value = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TnpointValueAtTimestamptz(this.Ptr, MEOSConvert.ToTimestampTz(t), strict, _value)) + { + return null; + } + + return MEOSFactory.WrapNpoint(Marshal.ReadIntPtr(_value)); + } + finally + { + Marshal.FreeHGlobal(_value); + } + } + + public Npoint? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TnpointValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapNpoint(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Npoint?[] Values() => MEOSFactory.WrapNpointArray(Meos.TnpointValues(this.Ptr)); diff --git a/MEOS.NET/Types/TPoint.g.cs b/MEOS.NET/Types/TPoint.g.cs index ae808f8..f17ed83 100644 --- a/MEOS.NET/Types/TPoint.g.cs +++ b/MEOS.NET/Types/TPoint.g.cs @@ -80,6 +80,24 @@ public double Length() public Temporal? Speed() => MEOSFactory.WrapTemporal(Meos.TpointSpeed(this.Ptr)); + public Geo? TfloatToGeomeas(Temporal measure, bool segmentize) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TpointTfloatToGeomeas(this.Ptr, measure.Ptr, segmentize, _result)) + { + return null; + } + + return MEOSFactory.WrapGeo(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Geo? Trajectory(bool unary_union) => MEOSFactory.WrapGeo(Meos.TpointTrajectory(this.Ptr, unary_union)); diff --git a/MEOS.NET/Types/TPose.g.cs b/MEOS.NET/Types/TPose.g.cs index 841eb7c..b756100 100644 --- a/MEOS.NET/Types/TPose.g.cs +++ b/MEOS.NET/Types/TPose.g.cs @@ -89,6 +89,42 @@ public string AsGeoposeStreamElement(Temporal inst, int precision) public Geo? Trajectory() => MEOSFactory.WrapGeo(Meos.TposeTrajectory(this.Ptr)); + public Pose? ValueAtTimestamptz(DateTime t, bool strict) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TposeValueAtTimestamptz(this.Ptr, MEOSConvert.ToTimestampTz(t), strict, _result)) + { + return null; + } + + return MEOSFactory.WrapPose(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + + public Pose? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TposeValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapPose(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Pose?[] Values() => MEOSFactory.WrapPoseArray(Meos.TposeValues(this.Ptr)); diff --git a/MEOS.NET/Types/TRGeometry.g.cs b/MEOS.NET/Types/TRGeometry.g.cs index 174c739..a7333b3 100644 --- a/MEOS.NET/Types/TRGeometry.g.cs +++ b/MEOS.NET/Types/TRGeometry.g.cs @@ -256,6 +256,24 @@ public string Out() public Geo? Twcentroid() => MEOSFactory.WrapGeo(Meos.TrgeometryTwcentroid(this.Ptr)); + public Geo? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TrgeometryValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapGeo(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Temporal? Yaw() => MEOSFactory.WrapTemporal(Meos.TrgeometryYaw(this.Ptr)); diff --git a/MEOS.NET/Types/TText.g.cs b/MEOS.NET/Types/TText.g.cs index 7d94d1c..98d1866 100644 --- a/MEOS.NET/Types/TText.g.cs +++ b/MEOS.NET/Types/TText.g.cs @@ -51,6 +51,42 @@ public string Out() public Temporal? Upper() => MEOSFactory.WrapTemporal(Meos.TtextUpper(this.Ptr)); + public Text? ValueAtTimestamptz(DateTime t, bool strict) + { + IntPtr _value = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TtextValueAtTimestamptz(this.Ptr, MEOSConvert.ToTimestampTz(t), strict, _value)) + { + return null; + } + + return MEOSFactory.WrapText(Marshal.ReadIntPtr(_value)); + } + finally + { + Marshal.FreeHGlobal(_value); + } + } + + public Text? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TtextValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapText(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Text?[] Values() => MEOSFactory.WrapTextArray(Meos.TtextValues(this.Ptr)); diff --git a/MEOS.NET/Types/TextSet.g.cs b/MEOS.NET/Types/TextSet.g.cs index 06e9ff7..e04cce7 100644 --- a/MEOS.NET/Types/TextSet.g.cs +++ b/MEOS.NET/Types/TextSet.g.cs @@ -36,6 +36,24 @@ public string Out() public Set? Upper() => MEOSFactory.WrapSet(Meos.TextsetUpper(this.Ptr)); + public Text? ValueN(int n) + { + IntPtr _result = Marshal.AllocHGlobal(8); + try + { + if (!Meos.TextsetValueN(this.Ptr, n, _result)) + { + return null; + } + + return MEOSFactory.WrapText(Marshal.ReadIntPtr(_result)); + } + finally + { + Marshal.FreeHGlobal(_result); + } + } + public Text?[] Values() => MEOSFactory.WrapTextArray(Meos.TextsetValues(this.Ptr)); diff --git a/tools/objectgen.py b/tools/objectgen.py index af1d331..dca65e8 100644 --- a/tools/objectgen.py +++ b/tools/objectgen.py @@ -322,6 +322,19 @@ def map_return(self, f: dict, wrapper_ret: str) -> tuple[str, str] | None: return (f"{struct}[]", f"MEOSConvert.ToStructArray<{struct}>($)") return None + def wrapped_out_reader(self, pointee: str) -> tuple[str, int, str] | None: + """The reader for a value MEOS writes through a pointer to a pointer. + + The buffer holds the ADDRESS of the value rather than the value, so the + reader takes that address out of it and wraps it in the class the model + gives the type — the same wrap a return of that type gets.""" + if not pointee.endswith("**"): + return None + cls = self.m.class_for_ctype(pointee[:-1].strip()) + if cls is None: + return None + return (cls, 8, f"MEOSFactory.Wrap{cls}(Marshal.ReadIntPtr({{0}}))") + def value_struct(self, c_type: str) -> str | None: """The struct a single pointer to a scalar-only struct carries. @@ -408,7 +421,7 @@ def method_for(self, cls: str, entry: dict) -> Method | None: pointee = clean(next( (p["cType"] for p in f.get("params", []) if p["name"] == out_params[0]), "")) - reader = OUT_PARAM_READERS.get(pointee) + reader = OUT_PARAM_READERS.get(pointee) or self.wrapped_out_reader(pointee) if reader: result_out = (codegen.csharp_param_name(out_params[0]), reader) else: