This document provides a comprehensive reference for the Quantity<UnitTag> template class, the core abstraction in qtty-cpp.
Table of Contents
- Overview
- Template Class: Quantity
- UnitTraits Contract
- Exception Types
- User-Defined Literals
- Usage Examples
Overview
The Quantity<UnitTag> template provides type-safe physical quantities with compile-time unit checking. Each unit (Meter, Second, etc.) is a distinct instantiation of this template, preventing accidental mixing of incompatible units.
Header: include/qtty/ffi_core.hpp
Template Class: Quantity
template<typename UnitTag>
class Quantity;
Template Parameters
| Parameter | Description |
| UnitTag | An empty tag struct identifying the unit (e.g., MeterTag, SecondTag) |
Member Types
| Type | Description |
| unit_tag | Type alias for UnitTag |
Constructors
Default Constructor
Constructs a quantity with value 0.0.
Line Reference: Line 91 in ffi_core.hpp
Example:
Value Constructor
constexpr explicit Quantity(double value);
Constructs a quantity with the specified value.
Parameters:
- value - The numeric value in this unit
Line Reference: Line 92 in ffi_core.hpp
Example:
Note: The explicit keyword prevents implicit conversions from double, enforcing clear intent:
Meter m = 10.5;
Meter m(10.5);
Meter m{10.5};
Static Member Functions
unit_id()
static constexpr UnitId unit_id();
Returns the C FFI unit identifier for this unit type.
Line Reference: Lines 95-97 in ffi_core.hpp
Returns: UnitId constant from the qtty_ffi.h header
Example:
UnitId id = Meter::unit_id();
This function delegates to UnitTraits<UnitTag>::unit_id().
Member Functions
value()
constexpr double value() const;
Returns the raw numeric value of the quantity.
Line Reference: Lines 100-102 in ffi_core.hpp
Returns: The stored value as a double
Example:
Meter m(42.0);
std::cout << m.value() << " m\n";
to<TargetType>()
template<typename TargetType>
Quantity<typename ExtractTag<TargetType>::type> to() const;
Converts this quantity to a different unit of the same dimension.
Line Reference: Lines 105-118 in ffi_core.hpp
Template Parameters:
- TargetType - Can be either a tag type (e.g., KilometerTag) or a quantity type (e.g., Kilometer)
Returns: A new Quantity of the target type with converted value
Throws:
- IncompatibleDimensionsError - If units have different dimensions (e.g., length vs time)
- ConversionError - If the conversion produces an invalid value
Example:
Meter m(1000.0);
Kilometer km = m.
to<Kilometer>();
auto km1 = m.
to<Kilometer>();
auto km2 = m.
to<KilometerTag>();
Quantity< typename ExtractTag< TargetType >::type > to() const
Conversion Process:
- Create a qtty_quantity_t with the source value and unit ID
- Call qtty_quantity_convert() from the FFI layer
- Check status and throw exception if conversion fails
- Return new Quantity with converted value
abs()
Returns the absolute value of this quantity.
Line Reference: Lines 196-198 in ffi_core.hpp
Returns: A new Quantity with the absolute value
Example:
Meter m(-10.5);
Meter positive = m.
abs();
Arithmetic Operators
Addition (same unit)
Quantity operator+(const Quantity& other) const;
Line Reference: Lines 121-123 in ffi_core.hpp
Example:
Meter a(10.0), b(5.0);
Meter sum = a + b;
Subtraction (same unit)
Quantity operator-(const Quantity& other) const;
Line Reference: Lines 125-127 in ffi_core.hpp
Example:
Meter a(10.0), b(3.0);
Meter diff = a - b;
Scalar Multiplication
Quantity operator*(double scalar) const;
friend Quantity operator*(double scalar, const Quantity& q);
Line Reference: Lines 130-132, 139-141 in ffi_core.hpp
Example:
Meter m(10.0);
Meter doubled = m * 2.0;
Meter tripled = 3.0 * m;
Scalar Division
Quantity operator/(double scalar) const;
Line Reference: Lines 134-136 in ffi_core.hpp
Example:
Meter m(10.0);
Meter half = m / 2.0;
Quantity Division (creates compound units)
template<typename LengthTag, typename TimeTag>
Quantity<CompoundTag<LengthTag, TimeTag>> operator/(
const Quantity<LengthTag>& length,
const Quantity<TimeTag>& time);
Header: include/qtty/units/velocity.hpp (lines 22-28)
Example:
Meter distance(100.0);
Second time(20.0);
auto velocity = distance / time;
See Compound Units documentation for details.
Comparison Operators
All comparison operators work with quantities of the same unit type:
bool operator==(const Quantity& other) const;
bool operator!=(const Quantity& other) const;
bool operator<(const Quantity& other) const;
bool operator>(const Quantity& other) const;
bool operator<=(const Quantity& other) const;
bool operator>=(const Quantity& other) const;
Example:
Meter a(10.0), b(5.0), c(10.0);
bool eq = (a == c);
bool ne = (a != b);
bool lt = (b < a);
bool gt = (a > b);
bool le = (a <= c);
bool ge = (a >= c);
Compound Assignment Operators
Quantity& operator+=(const Quantity& other);
Quantity& operator-=(const Quantity& other);
Quantity& operator*=(double scalar);
Quantity& operator/=(double scalar);
Example:
Meter m(10.0);
m += 5.0_m;
m -= 3.0_m;
m *= 2.0;
m /= 4.0;
Unary Operators
Negation
Quantity operator-() const;
Line Reference: Lines 190-192 in ffi_core.hpp
Example:
Meter m(10.0);
Meter negated = -m;
UnitTraits Contract
The UnitTraits<> template must be specialized for each unit tag to provide the FFI unit ID.
Header: include/qtty/ffi_core.hpp (lines 64-66)
Template Specialization
template<typename UnitTag>
struct UnitTraits;
Required Member
static constexpr UnitId unit_id();
Returns the C FFI constant for this unit (e.g., UNIT_ID_METER, UNIT_ID_SECOND).
Example Specialization
template<> struct UnitTraits<MeterTag> {
static constexpr UnitId unit_id() { return UNIT_ID_METER; }
};
template<> struct UnitTraits<SecondTag> {
static constexpr UnitId unit_id() { return UNIT_ID_SECOND; }
};
Location: Generated headers in include/qtty/units/
All UnitTraits specializations are automatically generated by the Rust gen_cpp_units tool in gen_cpp_units/.
Exception Types
qtty-cpp uses a hierarchy of exception types for error handling. All exceptions derive from std::runtime_error.
Header: include/qtty/ffi_core.hpp (lines 14-35)
QttyException
class QttyException : public std::runtime_error;
Base class for all qtty-related exceptions.
Line Reference: Lines 14-17
InvalidUnitError
class InvalidUnitError : public QttyException;
Thrown when an unknown or invalid unit ID is encountered.
Line Reference: Lines 19-22
Example:
try {
qtty_quantity_t qty;
qtty_quantity_make(100.0, 99999, &qty);
check_status(status, "Invalid unit");
} catch (const InvalidUnitError& e) {
std::cerr << "Invalid unit: " << e.what() << '\n';
}
IncompatibleDimensionsError
class IncompatibleDimensionsError : public QttyException;
Thrown when attempting to convert between units of different dimensions.
Line Reference: Lines 24-27
Example:
try {
Meter m(100.0);
Second s = m.
to<Second>();
} catch (const IncompatibleDimensionsError& e) {
std::cerr << "Incompatible dimensions: " << e.what() << '\n';
}
ConversionError
class ConversionError : public QttyException;
Thrown when a conversion produces an invalid or out-of-range value.
Line Reference: Lines 29-32
Example:
try {
} catch (const ConversionError& e) {
std::cerr << "Conversion failed: " << e.what() << '\n';
}
check_status()
inline void check_status(int32_t status, const char* operation);
Helper function that translates C FFI status codes into C++ exceptions.
Line Reference: Lines 38-56 in ffi_core.hpp
Parameters:
- status - The status code returned by an FFI function
- operation - Descriptive text for error messages
Throws: Appropriate exception type based on status code
Status Code Mapping:
| FFI Status | C++ Exception |
| QTTY_OK | (no exception) |
| QTTY_ERR_UNKNOWN_UNIT | InvalidUnitError |
| QTTY_ERR_INCOMPATIBLE_DIM | IncompatibleDimensionsError |
| QTTY_ERR_NULL_OUT | QttyException |
| QTTY_ERR_INVALID_VALUE | ConversionError |
| Other | QttyException |
User-Defined Literals
User-defined literals provide intuitive syntax for creating quantities.
Header: include/qtty/literals.hpp
Literal Syntax
Literals are defined in an inline namespace for easy access:
inline namespace literals {
}
}
This allows:
Literal Operators
Each unit has two overloads to support both floating-point and integer literals:
constexpr Meter operator""_m(
long double value) {
return Meter(
static_cast<double>(value));
}
constexpr Meter operator""_m(
unsigned long long value) {
return Meter(
static_cast<double>(value));
}
Quantity< MeterTag > Meter
Available Literals
Length Literals
| Literal | Unit | Example |
| _m | Meter | 10.0_m |
| _km | Kilometer | 5.0_km |
| _mm | Millimeter | 100.0_mm |
| _cm | Centimeter | 50.0_cm |
| _um | Micrometer | 500.0_um |
| _nm | Nanometer | 750.0_nm |
| _in | Inch | 12.0_in |
| _ft | Foot | 3.0_ft |
| _mi | Mile | 1.0_mi |
| _au | Astronomical Unit | 1.0_au |
| _ly | Light Year | 1.0_ly |
| _pc | Parsec | 1.0_pc |
Time Literals
| Literal | Unit | Example |
| _s | Second | 30.0_s |
| _min | Minute | 5.0_min |
| _h | Hour | 2.0_h |
| _day | Day | 1.0_day |
| _ms | Millisecond | 100.0_ms |
| _us | Microsecond | 500.0_us |
| _ns | Nanosecond | 1000.0_ns |
Angular Literals
| Literal | Unit | Example |
| _rad | Radian | 3.14159_rad |
| _deg | Degree | 90.0_deg |
| _arcmin | Arcminute | 60.0_arcmin |
| _arcsec | Arcsecond | 3600.0_arcsec |
Mass Literals
| Literal | Unit | Example |
| _kg | Kilogram | 10.0_kg |
| _g | Gram | 1000.0_g |
| _mg | Milligram | 5000.0_mg |
| _ton | Metric Ton | 1.0_ton |
| _lb | Pound | 2.2_lb |
| _oz | Ounce | 16.0_oz |
Power Literals
| Literal | Unit | Example |
| _W | Watt | 100.0_W |
| _kW | Kilowatt | 5.0_kW |
| _MW | Megawatt | 1.0_MW |
| _hp | Horsepower | 1.0_hp |
Literal Suffix Collisions
When multiple units have the same symbol, the generator detects collisions and skips duplicate literals. Warnings are printed during generation:
Warning: Skipping literal _nm for ... (conflicts with Nanometer)
In such cases, use explicit constructors:
Quantity< NanometerTag > Nanometer
Usage Examples
Basic Construction and Access
std::cout << distance.value() << " m\n";
auto height = 5.5_m;
std::cout << height.value() << " m\n";
Main header for the qtty C++ wrapper library.
Unit Conversions
std::cout << km.
value() <<
" km\n";
std::cout << m2.
value() <<
" m\n";
auto angle = 180.0_deg;
std::cout << rad.
value() <<
" rad\n";
constexpr double value() const
Quantity< RadianTag > Radian
Quantity< KilometerTag > Kilometer
Arithmetic Operations
Comparisons
Meter a(10.0), b(5.0), c(10.0);
if (a > b) {
std::cout << "a is greater\n";
}
if (a == c) {
std::cout << "a equals c\n";
}
std::vector<Meter> distances = {10.0_m, 5.0_m, 15.0_m};
std::sort(distances.begin(), distances.end());
Velocity (Compound Units)
auto velocity = distance / time;
std::cout << velocity.value() << " m/s\n";
Quantity< SecondTag > Second
Quantity< CompoundTag< MeterTag, SecondTag > > MeterPerSecond
Error Handling
try {
std::cerr << "Error: " << e.what() << '\n';
std::cerr << "Qtty error: " << e.what() << '\n';
}
Raised when mixing incompatible dimensions in conversion/arithmetic.
Base exception for all qtty wrapper failures.
Working with Raw Values
double raw_value = m.value();
double processed = raw_value * 2.0 + 5.0;
Utility Functions
Meter positive = negative.abs();
Type Safety Examples
auto distance = 10.0_m;
auto time = 5.0_s;
auto velocity = distance / time;
See Also