Skip to content
This repository has been archived by the owner on May 27, 2022. It is now read-only.

Implement C# Generation and Runtime #71

Merged
merged 43 commits into from
Dec 1, 2020
Merged

Conversation

mattico
Copy link
Contributor

@mattico mattico commented Nov 13, 2020

Summary

A fairly straightforward port of the Java code to C#. Targets .NET Standard 1.6 for broad compatibility. Newer .NET versions do have some features that would be useful (Rune, record, Range), maybe we can switch in a few years.

There are a few notable changes compared to the Java version:

  1. C-style enums are optionally translated to a C# enum.
  2. Wrapper classes are used for maps and arrays to implement value equality.
  3. Builder classes are not emitted or used.
  4. Code is generated into a single project called Serde. Runtimes are in subfolders (and sub-namespaces) of that.

Fixes #68.

Test Plan

I ported the tests over from Java as well. Let's see if I got CI working on the first try.

@mattico mattico requested a review from ma2bd as a code owner November 13, 2020 21:02
@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 13, 2020
@ma2bd
Copy link
Contributor

ma2bd commented Nov 16, 2020

Thanks for the PR.

Couple of questions/comments while I'm getting started with the code review:

  1. I don't think we need to explicitly support hashmaps or hashsets where keys would have different Serde types. Do you have a specific use case in mind?
  2. I like this idea a lot but we don't do this in the other languages yet. Can we make this behavior optional (I can add a config option for you) so that we can coordinate a slow rollout across languages? Ideally, this would also be a separate issue and PR in C#.
  3. In Java, without a wrapper for Bytes, it's not really feasible to handle composed types like Map<Bytes, T>, (Bytes, T), let alone Map<(Bytes, T), S>. If the same observation holds for C# (as I would imagine), then it is a strong argument to use a wrapper for byte[]. FWIW protobuf's codegen introduces lots of wrappers around native types in Java and C# (seemingly also to provide a correct "value semantics"): https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/bytes-value
  4. Sure, we can add builders later. I believe this is the preferred style in Java (and C#?) these days, to avoid bugs where arguments of similar types are swapped by mistake.
  5. I haven't looked at the details yet but we may have change this to be consistent with Java at some point.

@mattico
Copy link
Contributor Author

mattico commented Nov 16, 2020

  1. Part of GetHashCodes contract is that it is congruent to Equals. Given that it is easy to implement correctly I would much rather do that than have to document somewhere that it doesn't work as expected which will inevitably cause problems for someone. I can imagine using an enum as a hashmap key, in my application I was thinking about using a ConfigKey enum which would be used as a key into a hashmap. If the concern is the dependency on rand I could hash the type or something.
  2. Fair enough.
  3. That's a good reason to use a wrapper. I had one in an earlier commit which I'll resurrect.
  4. I was wondering about the utility of the builder classes. In C# I would use keyword arguments or an object initializer for that.

@ma2bd
Copy link
Contributor

ma2bd commented Nov 17, 2020

  1. Part of GetHashCodes contract is that it is congruent to Equals. Given that it is easy to implement correctly I would much rather do that than have to document somewhere that it doesn't work as expected which will inevitably cause problems for someone. I can imagine using an enum as a hashmap key, in my application I was thinking about using a ConfigKey enum which would be used as a key into a hashmap. If the concern is the dependency on rand I could hash the type or something.

I believe the only strict requirement is that x1.equals(x2) implies x1.GetHashCode() == x2.GetHashCode().

In the examples that I could find over the internet for C# (and Java), people (or IntelliJ) always ignore the class of an object and just consume its fields one by one. (In C#, you have HashCode.Add to do so, apparently.)

The proposed improvement to use rand at codegen time for the seed is easy and kinda cool but for those who commit the result of the serde-generate in git (like us), it will create meaningless changed lines in git commits. I can live with a solution based on stable hashing of Serde types, but is it worth the effort?

  1. I was wondering about the utility of the builder classes. In C# I would use keyword arguments or an object initializer for that.

Sounds good to me!

Copy link
Contributor

@ma2bd ma2bd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the impressive work! I'll try to make another pass this week. Looking forward to releasing the C# support!

serde-generate/runtime/csharp/Serde/Option.cs Show resolved Hide resolved
serde-generate/src/config.rs Show resolved Hide resolved
serde-generate/src/csharp.rs Show resolved Hide resolved
serde-generate/src/csharp.rs Outdated Show resolved Hide resolved
serde-generate/src/csharp.rs Outdated Show resolved Hide resolved
serde-generate/src/csharp.rs Outdated Show resolved Hide resolved
serde-generate/src/lib.rs Outdated Show resolved Hide resolved
serde-generate/tests/csharp_generation.rs Show resolved Hide resolved
serde-generate/tests/csharp_runtime.rs Outdated Show resolved Hide resolved
serde-generate/tests/csharp_runtime.rs Show resolved Hide resolved
@mattico
Copy link
Contributor Author

mattico commented Nov 17, 2020

I believe the only strict requirement is that x1.equals(x2) implies x1.GetHashCode() == x2.GetHashCode().
...

You're correct, should've validated my assumptions :)

The proposed improvement to use rand at codegen time for the seed is easy and kinda cool but for those who commit the result of the serde-generate in git (like us), it will create meaningless changed lines in git commits. I can live with a solution based on stable hashing of Serde types, but is it worth the effort?

That's a good reason to avoid rand here. I'll revert that change. We can revisit this if anyone ever needs it.

@mattico
Copy link
Contributor Author

mattico commented Nov 18, 2020

Rebased, added value-equality test, and added argument parsing for --c-style-enums. Not totally happy with the argument parsing, open to suggestions.

@ma2bd
Copy link
Contributor

ma2bd commented Nov 19, 2020

Rebased, added value-equality test, and added argument parsing for --c-style-enums. Not totally happy with the argument parsing, open to suggestions.

Did you meant the panic!s? I think that's fine for now. We can always improve later.

I'm going to land this PR now (EDIT: actually I have one last question for you) and give us a few days before releasing a crate.

@mattico Thanks for the awesome contribution!

serde-generate/src/csharp.rs Outdated Show resolved Hide resolved
@mattico
Copy link
Contributor Author

mattico commented Nov 19, 2020

Also it appears that the C# test runner stopped being able to discover the tests and it succeeds when there's no tests found :|

@ma2bd ma2bd merged commit ed8dbc2 into novifinancial:master Dec 1, 2020
@ma2bd
Copy link
Contributor

ma2bd commented Dec 1, 2020

@mattico Thanks the latest changes. I just merged the PR!

@mattico mattico deleted the csharp branch December 1, 2020 05:37
@ma2bd
Copy link
Contributor

ma2bd commented Dec 1, 2020

@mattico fyi I started a PR to prepare the release: #75

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Add C# Code Generation
3 participants