Ensure Ultimate Safety¶
This guide shows how to combine mp-units safety features to get guaranteed, always-on constraint enforcement for your quantities — independent of build mode or compiler optimization level.
Background reading
For in-depth background on how quantity_bounds and overflow policies work, see
Range-Validated Quantity Points.
The Problem¶
By default, the library's bounds checking for quantity points uses
MP_UNITS_EXPECTS,
which may be compiled away in release builds. This is fine for catching logic errors
during development, but some domains need guaranteed enforcement:
- Safety-critical systems (aviation, medical, automotive)
- Input validation at system boundaries
- Financial calculations with strict ranges
The Solution¶
The library provides building blocks that work together:
quantity_bounds<Origin>— a customization point that attaches a validation policy to a quantity point originconstrained<T, ErrorPolicy>— a transparent wrapper that tags a representation type with an error policyconstraint_violation_handler<Rep>— a customization point that library features query to dispatch errors
The library ships several overflow policies
to assign to quantity_bounds: check_in_range, clamp_to_range, wrap_to_range,
and reflect_in_range. For guaranteed enforcement, this guide uses check_in_range,
which delegates to the constraint_violation_handler when one is available for
the representation type.
Step 1: Choose an Error Policy¶
The library ships two error policies in <mp-units/constrained.h>:
| Policy | Availability | Behavior |
|---|---|---|
throw_policy |
Hosted only | Throws std::domain_error |
terminate_policy |
Always | Calls std::abort() |
You can also write your own:
struct log_and_continue_policy {
static void on_constraint_violation(std::string_view msg) {
spdlog::error("Constraint violation: {}", msg);
}
};
Step 2: Use constrained<T, Policy> as Your Representation Type¶
Wrap your numeric type with the desired policy:
#include <mp-units/constrained.h>
using safe_double = mp_units::constrained<double, mp_units::throw_policy>;
constrained<T> is transparent: it implicitly converts to/from T and forwards all
arithmetic. But it carries the error policy as a compile-time tag that the library can detect.
The library automatically registers a constraint_violation_handler for every
constrained<T, EP> instantiation, forwarding violations to EP::on_constraint_violation().
Step 3: Attach Bounds to Your Origin¶
Specialize quantity_bounds for your origin with an appropriate overflow policy.
For guaranteed enforcement, use check_in_range:
#include <mp-units/overflow_policies.h>
#include <mp-units/framework.h>
#include <mp-units/systems/si.h>
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
inline constexpr struct geo_latitude final : quantity_spec<isq::angular_measure> {} geo_latitude;
inline constexpr struct equator final : absolute_point_origin<geo_latitude> {} equator;
template<>
inline constexpr auto mp_units::quantity_bounds<equator> = check_in_range{-90 * deg, 90 * deg};
Step 4: Combine Them¶
Use constrained<T> as the representation type in your quantity point:
using latitude = quantity_point<geo_latitude[deg], equator, safe_double>;
void process_coordinates(double raw_lat)
{
try {
latitude lat{raw_lat * deg, equator}; // throws if |raw_lat| > 90
// ... use lat safely ...
} catch (const std::domain_error& e) {
// handle invalid input
}
}
When check_in_range detects an out-of-bounds value, it finds the
constraint_violation_handler<constrained<double, throw_policy>> specialization and
calls throw_policy::on_constraint_violation("value out of bounds"), which throws
std::domain_error. This happens in every build mode — debug, release, or
release-with-debug-info.
Extensible policy interface
The quantity_bounds customization point accepts any callable policy with the
signature V operator()(V). The library ships six built-in policies — check_in_range,
clamp_to_range, wrap_to_range, reflect_in_range, check_non_negative, and
clamp_non_negative — and the interface is fully extensible. check_non_negative and
clamp_non_negative cover the common [0, +∞) halfline case; they are also applied
automatically to natural origins of non-negative ISQ quantity specs. For a fully custom
one-sided or asymmetric policy, see Custom Policies.
How It Works?¶
flowchart TB
A["quantity_point<br>construction / mutation"] --> B["quantity_bounds<Origin>::operator()"]
B --> D{"Policy type?"}
D -- "check_in_range" --> E{"Has constraint_<br>violation_handler<br>for Rep?"}
E -- "Yes<br>(e.g. constrained<T>)" --> F["handler::on_violation()"]
E -- "No<br>(plain rep)" --> G["MP_UNITS_EXPECTS<br>(may be no-op)"]
F --> H["ErrorPolicy::<br>on_constraint_violation()"]
D -- "clamp / wrap / reflect" --> I["Value transformed<br>(always active)"]
Bringing Your Own Handler¶
You don't have to use constrained<T> — any representation type can opt in by specializing
constraint_violation_handler:
// Your custom safe-double type
class my_safe_double { /* ... */ };
template<>
struct mp_units::constraint_violation_handler<my_safe_double> {
static void on_violation(std::string_view msg)
{
throw std::domain_error(std::string(msg));
}
};
Now any quantity_bounds policy that queries constraint_violation_handler
(such as check_in_range) will use your handler whenever a quantity_point
with my_safe_double rep goes out of bounds.
Combining with safe_int¶
For integer quantities, the library provides safe_int<T> in <mp-units/safe_int.h>,
which detects arithmetic overflow. You can use both together:
#include <mp-units/safe_int.h>
// Overflow-detecting integer for arithmetic operations
quantity distance = safe_int{42} * m; // arithmetic overflow detected at runtime
// Bounded quantity point with overflow-detecting rep
using altitude = quantity_point<isq::height[m], msl_origin, safe_int<int>>;
safe_int also specializes constraint_violation_handler, so check_in_range will
use its error policy for bounds violations.
See Also¶
- Representation Types —
constraint_violation_handlerreference - Range-Validated Quantity Points — overflow policies reference
- Using Custom Representation Types — creating your own rep types