Skip to content

Enum can be deserialized from raw integers that don't map to enum values #714

Description

@cacharle

Any integer value is accepted to deserialize enums which is imo a risky default behavior since it means enum values absolutely need to be checked after deserialization by reflect-cpp.

Reproduction of the behavior:

// Minimal repro: reflect-cpp accepts an out-of-range enum value when it is
// supplied as a numeric string during the default (name-based) deserialization.
//
// Root cause: rfl::string_to_enum (include/rfl/enums.hpp) falls back to
//     static_cast<EnumType>(std::stoi(name))
// whenever the string is not a known enumerator name but IS parseable as an
// integer, with no check that the result is a defined enumerator.

#include <iostream>
#include <rfl.hpp>
#include <rfl/json.hpp>

enum class Color { red, green, blue };  // valid underlying values: 0, 1, 2

struct Config {
    Color color;
};

static void try_read(const char* json) {
    const auto res = rfl::json::read<Config>(json);
    if (res) {
        std::cout << "  " << json << "  -> ACCEPTED, color = "
                  << static_cast<int>(res->color)
                  << (static_cast<int>(res->color) > 2 ? "   <-- OUT OF RANGE, no error!" : "")
                  << "\n";
    } else {
        std::cout << "  " << json << "  -> rejected: " << res.error().what() << "\n";
    }
}

int main() {
    try_read(R"({"color":"green"})");   // valid name          -> accepted (1)
    try_read(R"({"color":"purple"})");  // invalid name        -> rejected (correct)
    try_read(R"({"color":"5"})");       // numeric string, OOR -> WRONGLY accepted (5)
    try_read(R"({"color":"-1"})");      // numeric string, OOR -> WRONGLY accepted (-1)
    return 0;
}

Outputs:

  {"color":"green"}  -> ACCEPTED, color = 1
  {"color":"purple"}  -> rejected: Failed to parse field 'color': stoi
  {"color":"5"}  -> ACCEPTED, color = 5   <-- OUT OF RANGE, no error!
  {"color":"-1"}  -> ACCEPTED, color = -1

I think ideally, the default behavior should change to not allow number or at least not out of range numbers.

If this can't be done because it would be a breaking change, a preprocessor would be a nice addition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions