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: