Wide Compatibility¶
This guide shows you how to write mp-units code that compiles across a wide range of compilers and C++ standards. You'll learn about the compatibility macros provided by the library and how to choose between terse, modern code and portable code that works everywhere.
While mp-units allows you to write clean, modern code for the latest C++ standards, the library also provides compatibility macros to support older environments. This guide helps you make the right trade-offs for your project.
Configuration Dependency
The behavior of compatibility macros depends on configuration options set during library installation. See Conan options and CMake options in the Installation and Usage guide for details on how to configure the library.
Choosing Your Compatibility Level¶
The library supports various levels of C++ standard conformance and formatting library choices. Depending on your requirements, you can choose to write:
- Modern, terse code targeting C++23 with modules
- Portable code using preprocessor macros for wide compatibility
- Formatting library - choose between
std::formator {fmt} based on your needs (features, compile times, or compiler support) - Something in between based on your minimum supported environment
Examples Use Compatibility Macros
The library's example applications use compatibility macros
because they're built on all supported compilers,
some of which don't yet support std::format, C++ modules, or C++ versions newer than C++20.
Code Style Options¶
Here are the different ways you can write the same code, ordered from most modern to most compatible:
#include <format>
#include <iostream>
import mp_units;
// ...
// C++20 CRTP syntax (explicit type parameter)
inline constexpr struct horizontal_length final :
quantity_spec<horizontal_length, isq::length> {} horizontal_length;
// ...
quantity q = horizontal_length(42 * m);
std::cout << std::format("length: {}\n", q);
#include <mp-units/systems/isq.h>
#include <mp-units/systems/si.h>
#include <format>
#include <iostream>
// ...
// C++20 CRTP syntax
inline constexpr struct horizontal_length final :
quantity_spec<horizontal_length, isq::length> {} horizontal_length;
// ...
quantity q = horizontal_length(42 * m);
std::cout << std::format("length: {}\n", q);
#include <mp-units/systems/isq.h>
#include <mp-units/systems/si.h>
#include <fmt/format.h>
#include <iostream>
// ...
// C++20 CRTP syntax
inline constexpr struct horizontal_length final :
quantity_spec<horizontal_length, isq::length> {} horizontal_length;
// ...
quantity q = horizontal_length(42 * m);
std::cout << fmt::format("length: {}\n");
#include <iostream>
#include <mp-units/ext/format.h>
#ifdef MP_UNITS_MODULES
#include <mp-units/compat_macros.h>
import mp_units;
#else
#include <mp-units/systems/isq.h>
#include <mp-units/systems/si.h>
#endif
// ...
// Works with both C++23 and C++20
QUANTITY_SPEC(horizontal_length, isq::length);
// ...
// Works with both std::format and {fmt}
quantity q = horizontal_length(42 * m);
std::cout << MP_UNITS_STD_FMT::format("length: {}\n");
Formatting Library Choice
The examples above show different C++ standard versions and include/import styles.
The formatting library choice (std::format vs fmt::format) is independent of
the C++ version - you can use {fmt} with C++20,
C++23, or any other standard. Many projects prefer {fmt}
even with C++23 for its additional features, faster compile times, or consistency
with existing code.
Choose Based on Your Needs
- Targeting a specific compiler? Use the terse, modern style directly
- Need maximum portability? Use the compatibility macros throughout
- Formatting library? Choose
std::formator {fmt} based on features, compile times, and project needs - Transitioning? Start with compatibility macros and refactor later
Using Compatibility Macros¶
Including the Compatibility Header¶
When using C++ modules, compatibility macros must be explicitly included:
With legacy headers, the compat header is automatically included.
QUANTITY_SPEC(name, ...)¶
Problem: Quantity specification definitions use C++23's explicit object parameters to avoid CRTP (Curiously Recurring Template Parameter), which significantly simplifies the syntax.
Solution: This macro automatically uses the appropriate syntax for your C++ version:
// With macro - works on C++20 and C++23
QUANTITY_SPEC(horizontal_length, isq::length);
// Expands to:
// C++23: struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;
// C++20: struct horizontal_length final : quantity_spec<horizontal_length, isq::length> {} horizontal_length;
Configuration
The macro's behavior depends on the no_crtp
Conan option or MP_UNITS_API_NO_CRTP
CMake option.
MP_UNITS_STD_FMT¶
Problem: Projects need to choose between std::format
and {fmt} based on compiler support, desired features,
compile times, or project standards. Many projects prefer {fmt}
even when std::format is available.
Solution: This macro resolves to either the std or fmt namespace based on your
configuration:
#include <mp-units/ext/format.h> // Includes the right formatting header
// Use the macro instead of hardcoding std or fmt
std::cout << MP_UNITS_STD_FMT::format("{}", distance) << "\n";
Configuration
The macro's behavior depends on the std_format
Conan option or MP_UNITS_API_STD_FORMAT
CMake option.
Contract Checking Macros¶
Problem: The library uses contract checking (preconditions, postconditions, assertions) which can be provided by either gsl-lite or ms-gsl.
Solution: Use these macros for portable contract checks:
#include <mp-units/ext/contracts.h>
void process_speed(quantity<si::metre / si::second, int> speed)
{
MP_UNITS_EXPECTS(speed > 0 * m / s); // Precondition (always checked if enabled)
// ...
MP_UNITS_ASSERT(result > 0); // Assertion (always checked if enabled)
}
void debug_function()
{
MP_UNITS_EXPECTS_DEBUG(condition); // Only checked in debug builds
// ...
MP_UNITS_ASSERT_DEBUG(invariant); // Only checked in debug builds
}
Available macros:
MP_UNITS_EXPECTS(expr)- Precondition checkMP_UNITS_EXPECTS_DEBUG(expr)- Debug-only precondition checkMP_UNITS_ASSERT(expr)- Assertion checkMP_UNITS_ASSERT_DEBUG(expr)- Debug-only assertion check
Their behavior is consistent with gsl-lite contract checking.
Configuration
The contract checking implementation depends on the contracts
Conan option or MP_UNITS_API_CONTRACTS
CMake option.
Complete Example¶
Here's a complete example using all compatibility features:
#include <iostream>
#include <mp-units/ext/format.h>
#include <mp-units/ext/contracts.h>
#ifdef MP_UNITS_MODULES
#include <mp-units/compat_macros.h>
import mp_units;
#else
#include <mp-units/systems/isq.h>
#include <mp-units/systems/si.h>
#endif
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
// Define custom quantity specification with wide compatibility
QUANTITY_SPEC(flight_distance, isq::length);
quantity<flight_distance[km]> calculate_distance(quantity<isq::speed[km / h]> speed,
quantity<isq::time[h]> duration)
{
MP_UNITS_EXPECTS(speed > 0 * km / h);
MP_UNITS_EXPECTS(duration > 0 * h);
return flight_distance(speed * duration);
}
int main()
{
quantity speed = 850 * km / h;
quantity duration = 2.5 * h;
quantity distance = calculate_distance(speed, duration);
// Format output with portable macro
std::cout << MP_UNITS_STD_FMT::format("Flying at {} for {} covers {}\n",
speed, duration, distance);
}
Summary¶
mp-units provides flexibility in how you write code:
- Modern syntax for latest compilers: clean and terse
- Compatibility macros for wide support: verbose but portable
- Incremental adoption: start portable, refactor to modern later
Key compatibility tools:
QUANTITY_SPEC()- Works across C++20 and C++23MP_UNITS_STD_FMT- Works withstd::formator {fmt}- Contract macros - Works with gsl-lite or ms-gsl
- Module guards - Support both modules and headers
Choose the approach that best fits your project's requirements and minimum supported environment.
See Also¶
Getting Started:
- Installation and Usage - Configuration
options:
- Conan options - Control compatibility features via Conan
- CMake options - Control compatibility features via CMake
- C++ Compiler Support - Supported compilers and features
Examples:
- Examples - All examples use compatibility macros