Feature Discussion: Null-safe, noexcept accessors — eval_value, eval_array, eval_object
#5129
gitpaladin
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Motivation
In server-side C++ applications, JSON payloads received from external sources frequently contain
nullvalues, missing keys, or even entirely unexpected types (e.g., a string where an object was expected). The currentvalue()function provides a convenient way to access object elements with a default fallback, but it throwstype_error(306) when the receiver itself isnullor is not an object.Consider the following real-world scenario:
This forces developers to wrap every access in a type/null check:
Or worse, when navigating nested paths with JSON Pointer:
JavaScript doesn't have this problem. Optional chaining and nullish coalescing make this trivial:
The proposed
eval_value,eval_array, andeval_objectfunctions aim to bring similar ergonomics to C++ by treating any non-matching type (null, missing, wrong type) as equivalent to "not present" rather than as an error. Crucially, these functions never throw and can be markednoexcept, making them safe to use in any context without try-catch.Proposed API
1.
eval_value— noexcept value access with defaultUsage:
Semantics compared to
value():value()eval_value()*thisis a non-null object, key exists*thisis a non-null object, key missing*thisisnulltype_errornull*thisis another type (array, string…)type_errorThe key design decision is: any non-matching receiver type — including
null, arrays, strings, numbers, and booleans — is treated as "absent", not as an error. This mirrors how JavaScript's optional chaining propagatesnull/undefinedwithout throwing, and aligns with the defensive programming philosophy that server-side code should never crash due to malformed input — instead, it should degrade gracefully to zero/empty values.Because
eval_valuenever throws, it is markednoexcept. This provides a strong guarantee to callers and enables compiler optimizations.2.
eval_array— noexcept array access (returns const reference)Returns a const reference to the array found at the specified key/pointer. If the receiver is any non-object type (null, array, string, number…), the key is missing, or the resolved value is not an array, returns a reference to a static empty array. Never throws.
Usage:
Without
eval_array, the same code requires verbose guarding:3.
eval_object— noexcept object access (returns const reference)Analogous to
eval_array, but for objects. Returns a const reference to the object found at the specified key/pointer, or a static empty object if the receiver is any non-object type, the path is missing, or the resolved value is not an object. Never throws.Usage:
Possible Implementation Sketch
Below is a simplified implementation sketch to illustrate the approach. The actual implementation would follow the library's template conventions (SFINAE guards, transparent comparator support, etc.).
All three functions are
noexcept— they never throw, regardless of the receiver's type. If*thisis not an object, or the path cannot be resolved, or the resolved value is the wrong type, the functions silently return the default/empty fallback. This makes them safe to use in any context.Design Considerations
Why not just fix
value()to handle null?Changing the existing
value()behavior would be a breaking change — existing users may rely on thetype_errorexception for null receivers as part of their validation logic. Per the project's contribution guidelines, the public API of the 3.x.y series must not be broken. A new function name makes the intent explicit and preserves backward compatibility.Why
noexcept? Why not throw on type mismatch?In server-side programming, the data received from external sources is fundamentally untrusted. When a server receives
"hello"where it expected{"a": 1}, crashing or entering an exception handler is rarely the right response — the correct behavior is to treat it as "no valid data" and fall back to a sensible default (zero, empty string, empty array, etc.).This is the same philosophy behind JavaScript's optional chaining:
(null)?.adoesn't throw a TypeError — it simply evaluates toundefined. Theeval_*functions extend this principle to all non-matching types.Marking these functions
noexceptprovides:noexceptenables the compiler to skip exception-handling table generationnoexceptcontexts, destructors, move constructors, etc.Why return static empty containers in
eval_array/eval_object?Returning a
constreference to a static empty JSON array/object avoids allocation and allows the result to be used directly in range-based for loops. Since the reference isconst, there is no risk of accidental mutation of the static sentinel.Naming alternatives
The
eval_prefix is chosen to suggest "evaluate with safe navigation", but other names could work:eval_value/eval_array/eval_objectvalue_or/array_or/object_orsafe_value/safe_array/safe_objectopt_value/opt_array/opt_objectstd::optionalsemanticsCommunity feedback on naming is welcome.
Thread safety of static empty containers
The static empty
basic_jsonobjects used ineval_arrayandeval_objectareconstand initialized via constant initialization or at worst Meyers' singleton (thread-safe since C++11). This should be safe for concurrent use.Summary
All
eval_*functions arenoexcept. They never throw regardless of the receiver's type.noexcepteval_value(key, default)eval_value(ptr, default)eval_array(key)[][][][]eval_array(ptr)[][][][]eval_object(key){}{}{}{}eval_object(ptr){}{}{}{}Comparison with existing
value():value()eval_value()*thisis object, key exists, correct type*thisis object, key missing*thisisnull*thisis array, string, number, bool…nullThese functions would significantly reduce boilerplate in server-side C++ code that processes JSON from untrusted or loosely-typed sources, bringing the developer experience closer to JavaScript's optional chaining — while remaining fully backward-compatible with the existing API and providing a strong
noexceptguarantee.Beta Was this translation helpful? Give feedback.
All reactions