* Union visitors

* Fix up stuff

* Fix UT
This commit is contained in:
James Courtney 2022-09-30 23:36:18 -07:00 коммит произвёл GitHub
Родитель cd7b5f708a
Коммит 6ac06752af
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 7737 добавлений и 105 удалений

Просмотреть файл

@ -16,6 +16,7 @@
using System.Linq;
using FlatSharp.Compiler.Schema;
using FlatSharp.CodeGen;
namespace FlatSharp.Compiler.SchemaModel;
@ -176,6 +177,7 @@ public class UnionSchemaModel : BaseSchemaModel
}
// Switch methods.
this.WriteAcceptMethod(writer, innerTypes);
this.WriteSwitchMethod(writer, true, true, innerTypes);
this.WriteSwitchMethod(writer, true, false, innerTypes);
this.WriteSwitchMethod(writer, false, true, innerTypes);
@ -183,6 +185,35 @@ public class UnionSchemaModel : BaseSchemaModel
}
}
private void WriteAcceptMethod(
CodeWriter writer,
List<(string resolvedType, EnumVal value)> components)
{
string visitorBaseType = $"IFlatBufferUnionVisitor<TReturn, {string.Join(", ", components.Select(x => x.resolvedType))}>";
writer.AppendSummaryComment("A convenience interface for implementing a visitor.");
writer.AppendLine($"public interface Visitor<TReturn> : {visitorBaseType} {{ }}");
writer.AppendSummaryComment("Accepts a visitor into this FlatBufferUnion.");
writer.AppendLine($"public TReturn Accept<TVisitor, TReturn>(TVisitor visitor)");
writer.AppendLine($" where TVisitor : {visitorBaseType}");
using (writer.WithBlock())
{
writer.AppendLine("var disc = this.Discriminator;");
writer.AppendLine("switch (disc)");
using (writer.WithBlock())
{
foreach (var item in components)
{
long index = item.value.Value;
writer.AppendLine($"case {index}: return visitor.Visit(({item.resolvedType})this.value);");
}
writer.AppendLine($"default: throw new {typeof(InvalidOperationException).GetCompilableTypeName()}(\"Unexpected discriminator: \" + disc);");
}
}
}
private void WriteSwitchMethod(
CodeWriter writer,
bool hasReturn,

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright 2020 James Courtney
* Copyright 2022 James Courtney
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,6 +32,29 @@ public interface IFlatBufferUnion
byte Discriminator { get; }
}
<#
for (int i = 1; i <= numGenerics; ++i)
{
#>
public interface IFlatBufferUnionVisitor<TReturn, <#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"T{x}")) #>>
{
<#
for (int j = 1; j <= i; ++j)
{
#>
TReturn Visit(T<#= j #> item);
<#
}
#>
}
<#
}
#>
<#
for (int i = 0; i < numGenerics; ++i)
@ -47,6 +70,9 @@ public interface IFlatBufferUnion
<# foreach (var genericType in range) { #>
T<#= genericType #> Item<#= genericType #> { get; }
<# } #>
//TReturn Accept<TVisitor, TReturn>(TVisitor visitor)
// where TVisitor : IFlatBufferUnionVisitor<TReturn, <#= genericList #>>;
}
[ExcludeFromCodeCoverage]
@ -110,6 +136,21 @@ public interface IFlatBufferUnion
<#
}
#>
public TReturn Accept<TVisitor, TReturn>(TVisitor visitor)
where TVisitor : IFlatBufferUnionVisitor<TReturn, <#= genericList #>>
{
switch (this.discriminator)
{
<# foreach (var genericType in range) { #>
case <#= genericType #>:
return visitor.Visit((T<#= genericType #>)this.value);
<# } #>
default:
throw new InvalidOperationException("Unexpected discriminator: " + this.discriminator);
}
}
public void Switch(
System.Action defaultCase,

Просмотреть файл

@ -49,18 +49,20 @@ public class UnionTests
string[] expectedAliases = new[] { "First", "B", "Foobar_C" };
// Validate nested enum
Type nestedEnum = unionType.GetNestedTypes().Single();
Type nestedEnum = unionType.GetNestedTypes().Where(x => x.IsEnum).Single();
Assert.True(nestedEnum.IsEnum);
Assert.Equal("ItemKind", nestedEnum.Name);
Assert.Equal(typeof(byte), Enum.GetUnderlyingType(nestedEnum));
Assert.Equal(types.Length + 1, Enum.GetValues(nestedEnum).Length);
Assert.Equal("NONE", Enum.GetName(nestedEnum, (byte)0));
for (int i = 0; i < types.Length; ++i)
{
Assert.Equal(expectedAliases[i], Enum.GetName(nestedEnum, (byte)(i + 1)));
}
Type nestedVistior = unionType.GetNestedTypes().Where(x => x.IsInterface).Single();
// Custom union defines ctors for all input types.
foreach (var type in types)
{

Просмотреть файл

@ -40,6 +40,8 @@
<Compile Include="ValueStructs\ValueStructs.fbs.generated.cs" />
<Compile Remove="Documentation\Documentation.fbs.generated.cs" />
<Compile Include="Documentation\Documentation.fbs.generated.cs" />
<Compile Remove="Unions\Unions.fbs.generated.cs" />
<Compile Include="Unions\Unions.fbs.generated.cs" />
</ItemGroup>
<Target Name="FBS" BeforeTargets="CoreCompile">
@ -50,6 +52,7 @@
<InputFile Include="Grpc\Grpc.fbs" />
<InputFile Include="ValueStructs\ValueStructs.fbs" />
<InputFile Include="Documentation\Documentation.fbs" />
<InputFile Include="Unions\Unions.fbs" />
</ItemGroup>
<Exec Command="dotnet $(FlatSharpCompilerDll) --nullable-warnings true -i %(InputFile.Identity) -o %(InputFile.RelativeDir)" />
</Target>

Просмотреть файл

@ -0,0 +1,17 @@

attribute "fs_serializer";
attribute "fs_sharedString";
attribute "fs_rpcInterface";
namespace FlatSharpEndToEndTests.Unions;
struct A { V : uint; }
struct B { V : uint; }
struct C { V : uint; }
struct D { V : uint; }
union MyUnion { A, B, C, D }
table Container (fs_serializer) {
Value : [MyUnion];
}

Просмотреть файл

@ -0,0 +1,163 @@
/*
* Copyright 2022 James Courtney
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace FlatSharpEndToEndTests.Unions;
public class UnionsTestCases
{
[Fact]
public void Union_Accept_Works()
{
var c = this.Setup();
UnionVisitor visitor = new();
Assert.Equal(typeof(A), c.Value[0].Accept<UnionVisitor, Type>(visitor));
Assert.Equal(typeof(B), c.Value[1].Accept<UnionVisitor, Type>(visitor));
Assert.Equal(typeof(C), c.Value[2].Accept<UnionVisitor, Type>(visitor));
Assert.Equal(typeof(D), c.Value[3].Accept<UnionVisitor, Type>(visitor));
}
[Fact]
public void Union_Switch_Func_Works()
{
static Type? CallSwitch(MyUnion union)
{
return union.Switch(
caseDefault: () => null,
caseA: a => typeof(A),
caseB: b => typeof(B),
caseC: c => typeof(C),
caseD: d => typeof(D));
}
var c = this.Setup();
Assert.Equal(typeof(A), CallSwitch(c.Value[0]));
Assert.Equal(typeof(B), CallSwitch(c.Value[1]));
Assert.Equal(typeof(C), CallSwitch(c.Value[2]));
Assert.Equal(typeof(D), CallSwitch(c.Value[3]));
}
[Fact]
public void Union_Switch_Func_WithState_Works()
{
static Type? CallSwitch(MyUnion union)
{
string? state = null;
Type? type = union.Switch(
"foobar",
caseDefault: (s) => { state = s; return (Type)null; },
caseA: (s, a) => { state = s; return typeof(A); },
caseB: (s, b) => { state = s; return typeof(B); },
caseC: (s, c) => { state = s; return typeof(C); },
caseD: (s, d) => { state = s; return typeof(D); });
Assert.Equal("foobar", state);
return type;
}
var c = this.Setup();
Assert.Equal(typeof(A), CallSwitch(c.Value[0]));
Assert.Equal(typeof(B), CallSwitch(c.Value[1]));
Assert.Equal(typeof(C), CallSwitch(c.Value[2]));
Assert.Equal(typeof(D), CallSwitch(c.Value[3]));
}
[Fact]
public void Union_Switch_Action_Works()
{
static Type? CallSwitch(MyUnion union)
{
Type? value = null;
union.Switch(
caseDefault: () => { },
caseA: a => { value = typeof(A); },
caseB: b => { value = typeof(B); },
caseC: c => { value = typeof(C); },
caseD: d => { value = typeof(D); });
return value;
}
var c = this.Setup();
Assert.Equal(typeof(A), CallSwitch(c.Value[0]));
Assert.Equal(typeof(B), CallSwitch(c.Value[1]));
Assert.Equal(typeof(C), CallSwitch(c.Value[2]));
Assert.Equal(typeof(D), CallSwitch(c.Value[3]));
}
[Fact]
public void Union_Switch_Action_WithState_Works()
{
static Type? CallSwitch(MyUnion union)
{
string? state = null;
Type? type = null;
union.Switch(
"foobar",
caseDefault: (s) => { state = s; },
caseA: (s, a) => { state = s; type = typeof(A); },
caseB: (s, b) => { state = s; type = typeof(B); },
caseC: (s, c) => { state = s; type = typeof(C); },
caseD: (s, d) => { state = s; type = typeof(D); });
Assert.Equal("foobar", state);
return type;
}
var c = this.Setup();
Assert.Equal(typeof(A), CallSwitch(c.Value[0]));
Assert.Equal(typeof(B), CallSwitch(c.Value[1]));
Assert.Equal(typeof(C), CallSwitch(c.Value[2]));
Assert.Equal(typeof(D), CallSwitch(c.Value[3]));
}
private Container Setup()
{
Container c = new Container
{
Value = new MyUnion[]
{
new MyUnion(new A()),
new MyUnion(new B()),
new MyUnion(new C()),
new MyUnion(new D()),
}
};
byte[] buffer = new byte[Container.Serializer.GetMaxSize(c)];
Container.Serializer.Write(buffer, c);
return Container.Serializer.Parse(buffer);
}
private struct UnionVisitor : MyUnion.Visitor<Type>
{
public Type Visit(A item) => typeof(A);
public Type Visit(B item) => typeof(B);
public Type Visit(C item) => typeof(C);
public Type Visit(D item) => typeof(D);
}
}

Просмотреть файл

@ -10,7 +10,7 @@
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>6.3.2</Version>
<PackageVersion>6.3.2</PackageVersion>
<PackageVersion>6.3.3</PackageVersion>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Authors>James Courtney</Authors>
<Description>FlatSharp is a fast, idiomatic implementation of the FlatBuffer binary format.</Description>