Open aschmahmann opened 7 years ago
I am unable to reproduce the problem. Here's what I tried.
bug.bond
namespace bug
struct A
{
0: int32 f1;
}
struct B : A { }
bug.cs
namespace bug
{
using System;
using Bond;
using Bond.IO.Safe;
using Bond.Protocols;
internal static class Program
{
private static void Main()
{
var b = new B(0);
var ser = new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(B));
var ob = new OutputBuffer();
var cw = new CompactBinaryWriter<OutputBuffer>(ob);
ser.Serialize(b, cw);
Console.WriteLine($"Length: {ob.Data.Count}");
}
}
public partial class B
{
public B(int fieldVal)
{
f1 = fieldVal;
}
}
}
No exception is thrown, and the serialized data for f1 == 0
and f1 == 10
look correct to me.
Do you have code that causes this you can share?
Apologies, you are correct. The exception appears not to be directly caused by this however it appears related (described below). However, it does appear to be the case that no default constructor is created for be such that calling new B()
is illegal - I have no particular preferences as to whether this is intended behavior or not (especially because as I menitioned in #397 I'd rather the constructors used only by Bond be hidden from the rest of the program) but thought it might be worth pointing out the inconsistency.
The exception I was having trouble tracing down resulted from code like the following:
bug.bond
namespace bug
using ICustomList<T> = blob;
struct A<T> {}
struct B : A<ICustomList<int32>> {}
struct C : ICustomList<int32> {}
bug.cs (with <BondOptions>--using="ICustomList=bug.ICustomList<{0}>"</BondOptions>
in the csproj file)
namespace bug
{
using System;
using Bond;
using Bond.IO.Safe;
using Bond.Protocols;
public interface ICustomList<T>
{
}
internal static class Program
{
private static void Main()
{
Serializer<CompactBinaryWriter<OutputBuffer>> ser;
OutputBuffer ob;
CompactBinaryWriter<OutputBuffer> cw;
var c = new C();
ser = new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(C));
var b = new B();
ser = new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(B));
ob = new OutputBuffer();
cw = new CompactBinaryWriter<OutputBuffer>(ob);
ser.Serialize(c, cw);
Console.WriteLine($"Length: {ob.Data.Count}");
ob = new OutputBuffer();
cw = new CompactBinaryWriter<OutputBuffer>(ob);
ser.Serialize(b, cw);
Console.WriteLine($"Length: {ob.Data.Count}");
}
}
}
Creating the serializer for C works fine, but creating the serializer for B throws a very opaque error. After some digging the error appears to come from Reflection.cs line 638 and is occurs because the code wants ICustomList to be a Bond struct (i.e. annotated with [Bond.Schema]
), despite the fact that no data being serialized is even of the type ICustomList. It's a little strange that C works and B does not work, but mostly the opacity of the error is what's bothered me about this for a while (I had to download the Bond source code and hook up the projects to my code instead of using NuGet because the error was so difficult to track down).
Thanks for showing that the bug was not where I thought it was. I've got the Bond source code set up now (although the compiler is still from NuGet) so hopefully any errors/unclear exceptions I find should be reported more cleanly.
Thanks for the extra details. This looks like a combination of two known issues:
List<T>
results in invalid output instead of exception constructing a serializerYes, C# Bond needs much nicer error messages.
Sorry to be a pain about this again, but I just ran into another case like the original one that doesn't seem related at all to lists or vectors (possibly to "containers" and generics though). It seems like the lack of default constructor generation on empty structs can indeed cause compilation errors/exceptions.
Original Reported Bug: CodeGen not creating default constructor on subclasses as described above
bug.bond
namespace bug
struct A {}
struct B : A {}
struct Container {
0: B f1;
}
bug.cs
namespace bug
{
using System;
using Bond;
using Bond.IO.Safe;
using Bond.Protocols;
internal static class Program
{
private static void Ser<T>(T data)
{
Serializer<CompactBinaryWriter<OutputBuffer>> ser = new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(T));
OutputBuffer ob = new OutputBuffer();
CompactBinaryWriter<OutputBuffer> cw = new CompactBinaryWriter<OutputBuffer>(ob);
ser.Serialize(data, cw);
Console.WriteLine($"Length: {ob.Data.Count}");
}
private static void Main()
{
var b = new B(5);
Ser(b);
var bCont = new Container() { f1 = new B(6) };
Ser(bCont);
}
}
public partial class A
{
public A(int unused)
{}
}
public partial class B
{
public B(int unused):base(unused)
{ }
}
}
In this case the bug C# project cannot even compile since the generated C# has the following, where the error is that There is no argument given that corresponds to the required formal parameter 'unused' of 'B.B(int)'
[global::Bond.Schema]
[System.CodeDom.Compiler.GeneratedCode("gbc", "0.9.0.0")]
public partial class Container
{
[global::Bond.Id(0)]
public B f1 { get; set; }
public Container()
: this("bug.Container", "Container")
{}
protected Container(string fullName, string name)
{
f1 = new B();
}
}
If instead, Container is ContainerSystem.TypeInitializationException occurred ..... ArgumentException: Type 'bug.B' does not have a default constructor
(modified code below):
bug.bond
namespace bug
struct A {}
struct B : A {}
struct Container<T> {
0: T f1;
}
bug.cs
namespace bug
{
using System;
using Bond;
using Bond.IO.Safe;
using Bond.Protocols;
internal static class Program
{
private static void Ser<T>(T data)
{
Serializer<CompactBinaryWriter<OutputBuffer>> ser = new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(T));
OutputBuffer ob = new OutputBuffer();
CompactBinaryWriter<OutputBuffer> cw = new CompactBinaryWriter<OutputBuffer>(ob);
ser.Serialize(data, cw);
Console.WriteLine($"Length: {ob.Data.Count}");
}
private static void Main()
{
var b = new B(5);
Ser(b);
var bCont = new Container<B>() { f1 = new B(6) };
Ser(bCont);
}
}
public partial class A
{
public A(int unused)
{}
}
public partial class B
{
public B(int unused):base(unused)
{ }
}
}
Hope this is helpful, and thanks so much for your quick responses.
If you have a Bond struct A with field f1 and then create Bond struct B : A with no fields gbc will not generate a default constructor for B. This means that Bond is unable to process B properly (e.g. won't serialize B because it can't make a B to look for default values - a topic addressed in issue #263). This is particularly problematic if B's partial class has another constructor B(x,y), because then C# will not implicitly generate a default constructor.Edit by @chwarr: See updated report below