Skip to content

Error when writing un-enumerated named enum values (#2672) - #2675

Open
stephenberry wants to merge 1 commit into
mainfrom
fix-enum-write-error-2672
Open

Error when writing un-enumerated named enum values (#2672)#2675
stephenberry wants to merge 1 commit into
mainfrom
fix-enum-write-error-2672

Conversation

@stephenberry

Copy link
Copy Markdown
Owner

Summary

Fixes #2672 — a write/read asymmetry for named enums.

A named enum (glz::meta with enumerate(...) or keys/value arrays) whose value is not one of the enumerated values has no string name. The writer previously fell back to emitting the underlying integer, but the string formats validate enum names on read, so that output could not be read back:

enum class myenum : uint8_t { A = 1, B = 2 };
template <> struct glz::meta<myenum> {
   using T = myenum;
   static constexpr auto value = glz::enumerate("A", T::A, "B", T::B);
};

std::string s;
glz::write_json(myenum{}, s); // previously wrote "0"
myenum r{};
glz::read_json(r, s);         // then failed: expected_quote

Change

The named-enum writer now errors with error_code::unexpected_enum by default when the value has no registered name, consistent with Glaze's philosophy of validating on read (untrusted input) while treating writes as trusted/computer-generated. Since the name lookup already happens on write, this adds no real cost.

Applied to the four formats that share this asymmetry (write name-or-integer-fallback, validate names on read):

Format Before After
JSON / TOML / YAML / MessagePack wrote underlying integer unexpected_enum error
BEVE / CBOR / BSON wrote integer (round-trips) unchanged

For JSON the writer's can_error = false was removed so the error propagates through containers and structs (it now defaults to true, like the chrono writers).

Opt-out

Added the inheritable option enum_int_fallback (default false) to restore the previous integer-emitting behavior:

struct my_opts : glz::opts { bool enum_int_fallback = true; };
glz::write<my_opts{}>(myenum{}, s); // "0", as before

Tests & docs

  • 18 new cases in tests/reflection/enum_test.cpp: write errors on un-enumerated values across all four formats; error propagates through struct/vector; enumerated values and valid round-trips still work; BEVE still round-trips integers; and an enum_int_fallback opt-out suite.
  • Documented in README.md and docs/options.md.

All affected suites pass locally: enum_test (50), json_test (686), toml_test (389), yaml_test (668), msgpack_test (59), beve_test (381).

Note (separate, not addressed here)

CSV has a from<CSV, named_enum> (read) but no to<CSV, named_enum> (write) — writing a named enum via CSV does not compile. That is a distinct pre-existing gap, left out of scope.

A named enum (glz::meta with enumerate(...) or keys/value arrays) whose
value is not one of the enumerated values has no string name. The writer
previously fell back to emitting the underlying integer, but the string
formats (JSON, TOML, YAML, MessagePack) validate enum names on read, so
that output could not be read back (issue #2672).

Make the writer error with error_code::unexpected_enum by default for
those four formats, consistent with Glaze's read-side validation. Binary
formats (BEVE, CBOR, BSON) intentionally round-trip integers and are
unchanged.

Add the inheritable opt-out option `enum_int_fallback` (default false) to
restore the previous integer-emitting behavior when desired.

Includes regression tests across all four formats plus an option opt-out
suite, and documentation in the README and docs/options.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON serde asymmetry for un-enumerated enum values

1 participant