[Feature] Compact syntax for tagged unions/algebraic datatypes
What feature would you like to see?
Thanks to #17, it is possible to define an algebraic data type like this rust type
enum Foo { A, B(X), C(Y, Z) }
in hexpat, for example like this:
enum FooTag : u8 { A, B, C };
struct Foo {
FooTag tag;
match (tag) {
(FooTag::A): {}
(FooTag::B): { X val1; }
(FooTag::C): { Y val1; Z val2; }
}
} [[format("fmt_foo")]];
fn fmt_foo(auto ref data) {
match (data.tag) {
(FooTag::A): { return std::format("{}", data.tag); }
(FooTag::B): { return std::format("{}({})", data.tag, data.val1); }
(FooTag::C): { return std::format("{}({},{})", data.tag, data.val1, data.val2); }
}
};
I would like some way to reduce this boilerplate, so I can avoid repeating this for every single data type in the data format.
For example, the syntax of an enum could be expanded similarly to how it is done in rust:
enum Foo : u8 { A, B(X), C(Y, Z) };
where the u8 describes the type of the tag. And this would expand to something similar to the type above.
How will this feature be useful to you and others?
It would significantly reduce the amount of boilerplate needed when working with binary data that corresponds to algebraic data types.
Request Type
- [ ] I can provide a PoC for this feature or am willing to work on it myself and submit a PR
Additional context?
Alternatives
An alternative would be if this could be implemented using meta-programming, but I haven't figured out if that is possible with the current system.
I discovered that I can at least automate the formatting part using code like this:
fn fmt_enum(ref auto data) {
match (std::core::member_count(data)) {
(1): return std::format("{}",data.tag);
(2): return std::format("{}({})",data.tag, data.val1);
(3): return std::format("{}({},{})",data.tag, data.val1, data.val2);
(4): return std::format("{}({},{},{})",data.tag, data.val1, data.val2, data.val3);
(5): return std::format("{}({},{},{},{})",data.tag, data.val1, data.val2, data.val3, data.val4);
}
};
Now I only need to write this once and can use it for every algebraic data type.
why not use parameter packs? for example
fn fmt_enum(auto first, auto ... rest) {
u32 count = std::sizeof_pack(rest);
str result;
if (std::sizeof_pack(rest) > 0) {
result = std::format("{},",first);
str temp = fmt_enum(rest);
result = result + temp;
} else
result = std::format("{}",first);
return result;
};
str test = fmt_enum(1, 2, 334, 4, 565, 6,445,455);
std::print(test);
prints
I: 1,2,334,4,565,6,445,455
I: Pattern exited with code: 0
I: Evaluation took 0.0040361s
Not exactly what you want but may be able to adapt it.
@paxcut I was considering those, but I assumed that a format function only takes a single argument, so it wouldn't work?
Not for the format function, but as a way to construct the formatting string for the nth case so you don't have to list each one explicitly. I havent really checked if it is possible and you may be right about not being able to use them. it just seemed like a good fit but if you already tried it then it is probably not good
@paxcut If there was a way to enumerate all members in a struct it could work, but I couldn't find any function for that.
and no apparent way of how to implement such a thing. We have the member count and we can check if a suspected member exists, but no method returns a list of members or a list of types. If you can narrow the possible types and names then yes, it is possible (like if the member names are always a1 a2 a3 a4 .... but in general no. Another feature you could use are the enum ranges. by assigning values like
enum Ranges : u8 {
A=0,
B=1 ... 2,
C= 3 .. .5,
D=6 ... 9,
E=10 ... 15
};
Then you can use the ranges to map the arguments to an array.