Skip to content

Commit

Permalink
Updated MEOS.NET to comply with new version of MEOS (#1)
Browse files Browse the repository at this point in the history
* Updated to comply with new MEOS version
* Builder rewritten (and improved) in C#
    * Removed duplicate functions after codegen
    * Removed old python builder script
    * Builder now automatically puts DllPath in generated code
* Removed old C# generated functions
* Registering and handling exceptions and MEOS functions are called and are safe at runtime
* Changed accessibility modifiers to expose only what is needed
  • Loading branch information
Terraxel99 authored Feb 1, 2024
1 parent de4b06f commit ef9f68d
Show file tree
Hide file tree
Showing 55 changed files with 8,762 additions and 2,775 deletions.
46 changes: 29 additions & 17 deletions ExampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
using MEOS.NET.General;
using MEOS.NET.Exceptions;
using MEOS.NET.Lifecycle;
using MEOS.NET.Types.General;

var timezone = "UTC";
MEOSLifecycle.Initialize(timezone);

var temporals = new List<TemporalGeometryPoint>()
try
{
TemporalGeometryPoint.From("[POINT(1 5)@2021-05-02, POINT(12 2)@2021-06-02]"),
TemporalGeometryPoint.From("POINT(11 3)@2023-08-06 01:45:00+00:00"),
TemporalGeometryPoint.From("[POINT(35 12)@2023-01-01, POINT(36 14)@2023-01-02]"),
};
var temporals = new List<TemporalGeometryPoint>()
{
TemporalGeometryPoint.From("[POINT(1 5)@2021-05-02, POINT(12 2)@2021-06-02]"),
TemporalGeometryPoint.From("POINT(11 3)@2023-08-06 01:45:00+00:00"),
TemporalGeometryPoint.From("[POINT(35 12)@2023-01-01, POINT(36 14)@2023-01-02]"),
};

var reference = TemporalGeometryPoint.From("[POINT(1 5)@2021-05-02, POINT(12 2)@2021-06-02]");
var reference = TemporalGeometryPoint.From("[POINT(1 5)@2021-05-02, POINT(12 2)@2021-06-02]");

for (int i = 0; i < temporals.Count; i++)
{
var text = (temporals[i] == reference) ? "equal" : "not equal";
Console.WriteLine($"The {i + 1}th temporal element is {text} to the reference temporal");
}
for (int i = 0; i < temporals.Count; i++)
{
var text = (temporals[i] == reference) ? "equal" : "not equal";
Console.WriteLine($"The {i + 1}th temporal element is {text} to the reference temporal");
}

// Sequence set with stepwise interpolation
var tempSeqSet = TemporalGeometryPoint.From("Interp=Step;{[POINT(1 1)@2000-01-01, POINT(2 2)@2000-01-02],[POINT(3 3)@2000-01-03, POINT(3 3)@2000-01-04]}");
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "json_output.json");
File.WriteAllText(path, tempSeqSet.ToJson());
// Sequence set with stepwise interpolation
// var tempSeqSet = TemporalGeometryPoint.From("Interp=Step;{[POINT(1 1)@2000-01-01, POINT(2 2)@2000-01-02],[POINT(3 3)@2000-01-03, POINT(3 3)@2000-01-04]}");
// var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "json_output.json");
// File.WriteAllText(path, tempSeqSet.ToJson());

MEOSLifecycle.Terminate();
var erroneousInput = TemporalGeometryPoint.From("e[POINT(1 5)@2021-05-02, POINT(12 2)@2021-06-02]"); // Intentional input error to trigger exception
}
catch (MEOSException e)
{
Console.WriteLine($"An error occurred in MEOS : {e.Code} - {e.Message}");
}
finally
{
MEOSLifecycle.Terminate();
}
2 changes: 2 additions & 0 deletions MEOS.NET.Builder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore files that are generated by the builder
*.cs.txt
7 changes: 7 additions & 0 deletions MEOS.NET.Builder/Constants/BuilderVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MEOS.NET.Builder.Constants
{
internal static class BuilderVersion
{
internal static readonly string CurrentVersion = "0.0.2";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using MEOS.NET.Builder.Models;

namespace MEOS.NET.Builder.EqualityComparers
{
internal class CSFunctionDeclarationComparer : IEqualityComparer<CSFunctionDeclaration>
{
public bool Equals(CSFunctionDeclaration? first, CSFunctionDeclaration? other)
{
if (first is null || other is null)
{
throw new InvalidOperationException();
}

var argsIntersection = first.Arguments.Intersect(other.Arguments);
var argsAreIdentical = argsIntersection.Count() == first.Arguments.Count();

return argsAreIdentical
&& first.FunctionName == other.FunctionName
&& first.ReturnType == other.ReturnType;
}

public int GetHashCode(CSFunctionDeclaration obj)
=> $"{obj.ReturnType}{obj.FunctionName}{obj.Arguments}".GetHashCode();
}
}

19 changes: 19 additions & 0 deletions MEOS.NET.Builder/Exceptions/InvalidCDeclarationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MEOS.NET.Builder.Models;

namespace MEOS.NET.Builder.Exceptions
{
public class InvalidCDeclarationException : Exception
{
public string? ReturnType { get; set; }
public string? FunctionName { get; set; }
public string? Arguments { get; set; }

internal InvalidCDeclarationException(CFunctionDeclaration declaration)
{
this.ReturnType = declaration.ReturnType;
this.FunctionName = declaration.FunctionName;
this.Arguments = declaration.Arguments;
}
}
}

26 changes: 26 additions & 0 deletions MEOS.NET.Builder/MEOS.NET.Builder.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<None Remove="MEOSExposedFunctions.cs.txt" />
<None Remove="MEOSExternalFunctions.cs.txt" />
<None Remove="View\" />
<None Remove="Workflow\" />
<None Remove="Exceptions\" />
<None Remove="Models\" />
<None Remove="EqualityComparers\" />
</ItemGroup>
<ItemGroup>
<Folder Include="View\" />
<Folder Include="EqualityComparers\" />
</ItemGroup>
<ItemGroup>
<Compile Remove="output.cs" />
</ItemGroup>
</Project>
13 changes: 13 additions & 0 deletions MEOS.NET.Builder/Models/CFunctionDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace MEOS.NET.Builder.Models
{
internal class CFunctionDeclaration : FunctionDeclaration
{
internal string? Arguments { get; init; } = string.Empty;

internal bool HasUndefinedElements()
=> string.IsNullOrEmpty(this.ReturnType) ||
string.IsNullOrEmpty(this.FunctionName) ||
string.IsNullOrEmpty(this.Arguments);
}
}

26 changes: 26 additions & 0 deletions MEOS.NET.Builder/Models/CSFunctionArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace MEOS.NET.Builder.Models
{
internal class CSFunctionArgument
{
internal string Type { get; set; } = string.Empty;

internal string Name { get; set; } = string.Empty;

public override string ToString()
=> $"{this.Type} {this.Name}";

public override bool Equals(object? obj)
{
if (obj?.GetType() != this.GetType()) throw new ArgumentException();

var other = obj as CSFunctionArgument;
if (other == null) throw new ArgumentNullException();

return other.Name == this.Name
&& other.Type == this.Type;
}

public override int GetHashCode()
=> $"{this.Type} {this.Name}".GetHashCode();
}
}
20 changes: 20 additions & 0 deletions MEOS.NET.Builder/Models/CSFunctionDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace MEOS.NET.Builder.Models
{
internal class CSFunctionDeclaration : FunctionDeclaration
{
internal IEnumerable<CSFunctionArgument> Arguments { get; init; } = new List<CSFunctionArgument>();

internal string ToArgumentsWithTypeString()
{
var stringArgs = this.Arguments.Select((arg) => arg.ToString());
return string.Join(", ", stringArgs);
}

internal string ToArgumentsWithoutTypeString()
{
var stringArgs = this.Arguments.Select((arg) => arg.Name);
return string.Join(", ", stringArgs);
}
}
}

9 changes: 9 additions & 0 deletions MEOS.NET.Builder/Models/FunctionDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace MEOS.NET.Builder.Models
{
internal abstract class FunctionDeclaration
{
internal string? ReturnType { get; init; } = string.Empty;
internal string? FunctionName { get; init; } = string.Empty;
}
}

22 changes: 22 additions & 0 deletions MEOS.NET.Builder/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using MEOS.NET.Builder.Workflow;
using MEOS.NET.Builder.View;

Display.IntroductionMessage();

var cHeaderFilePath = new RetrieveFileWorkflow()
.RetrieveFileUntilValid(() => Display.RetrieveHeaderFileMessage());

var compiledDllFilePath = new RetrieveFileWorkflow()
.RetrieveFileUntilValid(() => Display.RetrieveCompiledLibraryFileMessage());

var cDeclarations = new ReadFileWorkflow()
.Run(cHeaderFilePath);

var csDeclarations = new MapDefinitionsWorkflow()
.MapCDeclaractions(cDeclarations);

new WriteDefinitionsFileWorkflow(csDeclarations, compiledDllFilePath)
.Write("MEOSExternalFunctions");

new WriteSafeExecutedMethodsFileWorkflow(csDeclarations)
.Write("MEOSExposedFunctions");
21 changes: 21 additions & 0 deletions MEOS.NET.Builder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# MEOS.NET Code Builder

This tool aims to help within the development of MEOS.NET.

## What is this project ?

As MEOS.NET is a wrapper for MEOS, the core component of MobilityDB, the functions of MEOS are natively called from the .NET code.

Since MEOS has a lot of features and that those features are updated regularly, we need a tool that "translates" the C declarations into C# methods declarations which can then be called from the compiled MEOS library. This subproject of the solution allows to generate the C# from the C header file of MEOS.

**This project has, then, nothing to do with the MEOS.NET library** since it is a development tool.

## Updating MEOS

This tool is great when updating MEOS since it avoids the developer to write all the C# declarations manually. Note that if MEOS has new types, enums, this builder might need minor modifications in order to work properly.

## Usage

Run ```dotnet build``` and then ```dotnet run``` in the project. When requested, provide the path to the "meos.h". Then, the "output.cs" file will be generated with all the required code.


File renamed without changes.
21 changes: 21 additions & 0 deletions MEOS.NET.Builder/View/Display.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace MEOS.NET.Builder.View
{
public static class Display
{
public static void IntroductionMessage()
=> Console.WriteLine(Messages.Introduction);

public static void RetrieveHeaderFileMessage()
=> Console.WriteLine(Messages.InputMEOSHeaderPath);

public static void RetrieveCompiledLibraryFileMessage()
=> Console.WriteLine(Messages.InputCompiledMEOSPath);

public static void FileNotFoundMessage(string filePath)
=> Console.WriteLine(Messages.FileNotFound(filePath));

public static void WriteMessage(string message)
=> Console.WriteLine(message);
}
}

17 changes: 17 additions & 0 deletions MEOS.NET.Builder/View/Messages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace MEOS.NET.Builder.View
{
public static class Messages
{
public const string Introduction = "Welcome to the builder for MEOS functions\n" +
"This is a tool helping to the development of MEOS.NET\n" +
"From a C header file, it will write the C# methods that will be called in MEOS.NET";

public const string InputMEOSHeaderPath = "Please enter the path to the MEOS header file...";

public const string InputCompiledMEOSPath = "Please enter the path to the compiled MEOS library (DLL/.so)...";

public static string FileNotFound(string path)
=> $"File was not found ({path})";
}
}

95 changes: 95 additions & 0 deletions MEOS.NET.Builder/Workflow/MapDefinitionsWorkflow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System.Text.RegularExpressions;

using MEOS.NET.Builder.EqualityComparers;
using MEOS.NET.Builder.Models;

namespace MEOS.NET.Builder.Workflow
{
internal class MapDefinitionsWorkflow
{
internal IEnumerable<CSFunctionDeclaration> MapCDeclaractions(IEnumerable<CFunctionDeclaration> declarations)
{
return declarations.Select(d =>
new CSFunctionDeclaration
{
Arguments = this.MapCArguments(d.Arguments!),
FunctionName = d.FunctionName,
ReturnType = this.MapCType(d.ReturnType!),
}
).Distinct(new CSFunctionDeclarationComparer());
}

private string MapCType(string cType)
{
// String type
var newType = cType.Replace("char*", "string");

// Pointer types
if (newType.Contains('*'))
{
return "IntPtr";
}

// Non-pointer types
newType = newType.Replace("ushort_t", "ushort");
newType = newType.Replace("uint32", "uint");
newType = newType.Replace("uint64", "ulong");
newType = newType.Replace("size_t", "ulong");

newType = newType.Replace("uint8_t", "byte");
newType = newType.Replace("int8", "short");
newType = newType.Replace("int32", "int");
newType = newType.Replace("int64", "double");

newType = newType.Replace("error_handler_fn", "ErrorHandlingMethod");
newType = newType.Replace("TimestampTz", "DateTimeOffset");
newType = newType.Replace("Timestamp", "DateTime");

newType = newType.Replace("Datum", "object");

newType = newType.Replace("TimeADT", "int");
newType = newType.Replace("DateADT", "long");

// Enums
newType = newType.Replace("interpType", "InterpolationType");

return newType;
}

private IEnumerable<CSFunctionArgument> MapCArguments(string args)
{
if (args.Trim() == "void")
{
return Enumerable.Empty<CSFunctionArgument>();
}

var splitted = args.Split(',');
var regexPattern = "(?:\\s*const)?\\s*([\\w\\s]+)\\s+(\\*{0,2})\\s*(\\w+)\\s*";

var mappedArgs = new List<CSFunctionArgument>();

foreach (var arg in splitted)
{
var match = Regex.Match(arg, regexPattern);

if (!match.Success)
{
continue;
}

var oldType = match.Groups[1]?.ToString().Trim() + match.Groups[2]?.ToString().Trim();
var newType = this.MapCType(oldType);

var argName = match.Groups[3]?.ToString().Trim();

mappedArgs.Add(new CSFunctionArgument {
Name = argName ?? string.Empty,
Type = newType,
});
}

return mappedArgs;
}
}
}

Loading

0 comments on commit ef9f68d

Please sign in to comment.