diff --git a/Schema.Cpp.Test/QuantityCppTests.cs b/Schema.Cpp.Test/QuantityCppTests.cs
index 1ed0c07..80fed5d 100644
--- a/Schema.Cpp.Test/QuantityCppTests.cs
+++ b/Schema.Cpp.Test/QuantityCppTests.cs
@@ -315,6 +315,59 @@ public void ADefaultIsConstructedTheWayTheVocabularyAcceptsIt()
AssertCompiles(configured, TargetOptions);
}
+ ///
+ /// The table says what a quantity's bytes are, which for a vector form is not its storage.
+ ///
+ ///
+ /// A magnitude is one number and reports the number. A vector form is two to four of them side
+ /// by side, and reporting Float for one would be false about the bytes rather than
+ /// merely vague - false in the way that reads as true, since a consumer asks the representation
+ /// how many lanes a member has. Told Float, it takes a Velocity3D's first four
+ /// bytes for the whole value and checks one component while the other two go unexamined.
+ ///
+ /// All five arities the vocabulary has are pinned rather than the two that would carry the
+ /// argument. Every one of them is a real quantity - 148 magnitudes, 27 signed scalars, and 8,
+ /// 22 and 7 of two, three and four components - so none of these arms is defensive code, and
+ /// a reader of this test can see the whole rule instead of inferring it from a sample of it.
+ ///
+ ///
+ [TestMethod]
+ public void TheTableSaysWhatAQuantitysBytesAre()
+ {
+ Schema schema = new();
+ SchemaClass body = schema.AddClass("Body".As())!;
+
+ // A magnitude and a signed scalar are both one number, which is why they report the same
+ // thing from different arities: what the representation answers is the shape of the bytes,
+ // not how many directions the quantity has.
+ (string Member, string Quantity, string Representation)[] members =
+ [
+ ("Mass", "Mass", "Float"),
+ ("Heading", "Heading", "Float"),
+ ("Drift", "Velocity2D", "Vector2"),
+ ("Velocity", "Velocity3D", "Vector3"),
+ ("Worldline", "Velocity4D", "Vector4"),
+ ];
+
+ foreach ((string member, string quantity, _) in members)
+ {
+ body.AddMember(member.As())!
+ .SetType(new Quantity { QuantityName = quantity.As() });
+ }
+
+ string table = Generate(Configured(schema), TargetOptions with { Reflection = true })
+ .Single(file => file.Key.Contains("reflection", StringComparison.Ordinal)).Value;
+
+ // Every one of them is declared a Quantity; what differs is what the bytes are.
+ Assert.AreEqual(members.Length, table.Split(".kind = TypeKind::Quantity").Length - 1);
+
+ foreach ((string member, _, string representation) in members)
+ {
+ Assert.Contains($".representation = TypeKind::{representation}", table,
+ StringComparison.Ordinal, $"{member} should be {representation}");
+ }
+ }
+
///
/// A schema of one class holding one quantity.
///
diff --git a/Schema.Cpp/CppReflection.cs b/Schema.Cpp/CppReflection.cs
index be77cfc..d7d64d3 100644
--- a/Schema.Cpp/CppReflection.cs
+++ b/Schema.Cpp/CppReflection.cs
@@ -130,13 +130,38 @@ internal static string Kind(BaseType type) =>
{
Semantic { Declaration: SchemaSemanticType declaration } => Kind(declaration.Representation()),
- // A quantity says what it is stored in directly, so there is no chain to walk: a
- // Velocity3D over a float is three floats, and the table's reader needs the float.
- Quantity quantity => Kind(quantity.Storage),
+ Quantity quantity => QuantityRepresentation(quantity),
_ => Kind(type),
};
+ ///
+ /// What the bytes of a quantity are.
+ ///
+ ///
+ ///
+ /// A magnitude or a signed scalar is one number, so it is the number it is stored in. A vector
+ /// form is two to four of them laid out side by side, which is a Vector2, Vector3
+ /// or Vector4 - and saying Float for one would be false about the bytes, not
+ /// merely less specific.
+ ///
+ ///
+ /// It is false in a way that reads as true, which is why it is worth spelling out. A consumer
+ /// walking the table asks the representation how many lanes a member has; told Float, it
+ /// takes a Velocity3D's first four bytes for the whole value and checks one component
+ /// against the member's range while the other two go unexamined. A check that quietly examines
+ /// a third of what it was given is worse than one that declines.
+ ///
+ ///
+ private static string QuantityRepresentation(Quantity quantity) =>
+ quantity.Resolved?.Components switch
+ {
+ 2 => Kind(new Vector2()),
+ 3 => Kind(new Vector3()),
+ 4 => Kind(new Vector4()),
+ _ => Kind(quantity.Storage),
+ };
+
///
/// How a member's interpolation is named in the table.
///