qtty-cpp
Header-only C++ wrapper for qtty
Loading...
Searching...
No Matches
ffi_core.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <cmath>
9#include <stdexcept>
10#include <string>
11#include <type_traits>
12extern "C" {
13#include "qtty_ffi.h"
14}
15
16namespace qtty {
17
18// ============================================================================
19// Exception Hierarchy
20// ============================================================================
21// All qtty exceptions derive from std::runtime_error for compatibility with
22// standard exception handling practices. The hierarchy provides fine-grained
23// error types for different failure modes when interacting with the FFI layer.
24
25// Exception types for error handling
29class QttyException : public std::runtime_error {
30public:
31 explicit QttyException(const std::string& msg) : std::runtime_error(msg) {}
32};
33
38public:
39 explicit InvalidUnitError(const std::string& msg) : QttyException(msg) {}
40};
41
46public:
47 explicit IncompatibleDimensionsError(const std::string& msg) : QttyException(msg) {}
48};
49
54public:
55 explicit ConversionError(const std::string& msg) : QttyException(msg) {}
56};
57
58// ============================================================================
59// Error Translation from C FFI to C++ Exceptions
60// ============================================================================
61// Maps C-style integer error codes from the Rust FFI layer to typed C++
62// exceptions. This provides idiomatic error handling for C++ users while
63// maintaining compatibility with the C FFI boundary.
64
65// Helper function to check status and throw appropriate exceptions
72inline void check_status(int32_t status, const char* operation) {
73 if (status == QTTY_OK) {
74 return;
75 }
76
77 std::string msg = std::string(operation) + " failed: ";
78 switch (status) {
79 case QTTY_ERR_UNKNOWN_UNIT:
80 throw InvalidUnitError(msg + "unknown unit");
81 case QTTY_ERR_INCOMPATIBLE_DIM:
82 throw IncompatibleDimensionsError(msg + "incompatible dimensions");
83 case QTTY_ERR_NULL_OUT:
84 throw QttyException(msg + "null output pointer");
85 case QTTY_ERR_INVALID_VALUE:
86 throw ConversionError(msg + "invalid value");
87 default:
88 throw QttyException(msg + "unknown error");
89 }
90}
91
92// ============================================================================
93// Forward Declarations and Type Traits
94// ============================================================================
95
96// Forward declarations
97template<typename UnitTag>
98class Quantity;
99
100// Template trait to get unit ID from unit tag
101// Each unit tag (e.g., MeterTag) must specialize this template to provide
102// its corresponding C FFI unit ID constant (e.g., UNIT_ID_METER).
103// Specializations are auto-generated in include/qtty/units/*.hpp
104template<typename UnitTag>
106
107// Helper to extract tag from either a tag or Quantity<Tag>
108// This allows .to<>() to accept both Quantity<KilometerTag> and KilometerTag,
109// making the API more flexible and user-friendly.
110template<typename T>
112 using type = T;
113};
114
115template<typename Tag>
116struct ExtractTag<Quantity<Tag>> {
117 using type = Tag;
118};
119
120// ============================================================================
121// Quantity Template Class
122// ============================================================================
123// The core abstraction representing a physical quantity with compile-time
124// type safety. Each instantiation (e.g., Quantity<MeterTag>) is a distinct
125// type, preventing accidental mixing of incompatible units at compile time.
126//
127// Key Design Decisions:
128// - Header-only for zero-cost abstraction (all code inlined)
129// - constexpr constructors enable compile-time quantity creation
130// - explicit constructor prevents implicit double-to-Quantity conversions
131// - Conversions go through the Rust FFI layer for correctness
132
133// Base Quantity template class
134template<typename UnitTag>
135class Quantity {
136private:
137 double m_value;
138
139public:
140 using unit_tag = UnitTag;
141
142 // Constructors
143 constexpr Quantity() : m_value(0.0) {}
144 constexpr explicit Quantity(double value) : m_value(value) {}
145
146 // Get the unit ID for this quantity type
147 static constexpr UnitId unit_id() {
149 }
150
151 // Get the raw value
152 constexpr double value() const {
153 return m_value;
154 }
155
156 // ========================================================================
157 // Unit Conversion
158 // ========================================================================
159 // Converts this quantity to a different unit of the same dimension.
160 // The conversion is performed by the Rust qtty-ffi library to ensure
161 // correctness and consistency with the authoritative conversion factors.
162 //
163 // Accepts either a tag type (e.g., KilometerTag) or a Quantity type
164 // (e.g., Kilometer) for convenience, thanks to the ExtractTag helper.
165 //
166 // Throws IncompatibleDimensionsError if attempting to convert between
167 // different dimensions (e.g., length to time).
168
169 // Convert to another unit type (accepts either Tag or Quantity<Tag>)
170 template<typename TargetType>
172 using TargetTag = typename ExtractTag<TargetType>::type;
173 qtty_quantity_t src_qty;
174 qtty_quantity_t dst_qty;
175
176 int32_t status = qtty_quantity_make(m_value, unit_id(), &src_qty);
177 check_status(status, "Creating source quantity");
178
179 status = qtty_quantity_convert(src_qty, UnitTraits<TargetTag>::unit_id(), &dst_qty);
180 check_status(status, "Converting units");
181
183 }
184
185 // ========================================================================
186 // Arithmetic Operators (Same Unit)
187 // ========================================================================
188 // Addition and subtraction only work between quantities of the same unit.
189 // This enforces dimensional correctness at compile time. To add quantities
190 // of different units, explicitly convert one to match the other first.
191
192 // Arithmetic operators - same unit
194 return Quantity(m_value + other.m_value);
195 }
196
198 return Quantity(m_value - other.m_value);
199 }
200
201 // ========================================================================
202 // Scalar Operations
203 // ========================================================================
204 // Multiplying or dividing a quantity by a scalar preserves the unit.
205 // E.g., 10 meters * 2 = 20 meters
206
207 // Scalar multiplication and division
208 Quantity operator*(double scalar) const {
209 return Quantity(m_value * scalar);
210 }
211
212 Quantity operator/(double scalar) const {
213 return Quantity(m_value / scalar);
214 }
215
216 // Friend function for scalar * quantity
217 friend Quantity operator*(double scalar, const Quantity& q) {
218 return q * scalar;
219 }
220
221 // ========================================================================
222 // Comparison Operators
223 // ========================================================================
224 // All standard comparison operators are provided for convenience.
225 // Comparisons only work between quantities of the same unit type,
226 // enforcing type safety at compile time.
227
228 // Comparison operators
229 bool operator==(const Quantity& other) const {
230 return m_value == other.m_value;
231 }
232
233 bool operator!=(const Quantity& other) const {
234 return m_value != other.m_value;
235 }
236
237 bool operator<(const Quantity& other) const {
238 return m_value < other.m_value;
239 }
240
241 bool operator>(const Quantity& other) const {
242 return m_value > other.m_value;
243 }
244
245 bool operator<=(const Quantity& other) const {
246 return m_value <= other.m_value;
247 }
248
249 bool operator>=(const Quantity& other) const {
250 return m_value >= other.m_value;
251 }
252
253 // ========================================================================
254 // Compound Assignment Operators
255 // ========================================================================
256 // Modify the quantity in place for efficiency.
257
258 // Compound assignment operators
260 m_value += other.m_value;
261 return *this;
262 }
263
265 m_value -= other.m_value;
266 return *this;
267 }
268
270 m_value *= scalar;
271 return *this;
272 }
273
275 m_value /= scalar;
276 return *this;
277 }
278
279 // ========================================================================
280 // Unary Operators and Utilities
281 // ========================================================================
282
283 // Unary operators
285 return Quantity(-m_value);
286 }
287
288 Quantity abs() const {
289 return Quantity(std::abs(m_value));
290 }
291};
292
293} // namespace qtty
Raised when value conversion fails at the FFI boundary.
Definition ffi_core.hpp:53
ConversionError(const std::string &msg)
Definition ffi_core.hpp:55
Raised when mixing incompatible dimensions in conversion/arithmetic.
Definition ffi_core.hpp:45
IncompatibleDimensionsError(const std::string &msg)
Definition ffi_core.hpp:47
Raised when an unknown or invalid unit identifier is used.
Definition ffi_core.hpp:37
InvalidUnitError(const std::string &msg)
Definition ffi_core.hpp:39
Base exception for all qtty wrapper failures.
Definition ffi_core.hpp:29
QttyException(const std::string &msg)
Definition ffi_core.hpp:31
constexpr Quantity()
Definition ffi_core.hpp:143
Quantity & operator*=(double scalar)
Definition ffi_core.hpp:269
bool operator<=(const Quantity &other) const
Definition ffi_core.hpp:245
bool operator!=(const Quantity &other) const
Definition ffi_core.hpp:233
bool operator<(const Quantity &other) const
Definition ffi_core.hpp:237
Quantity operator+(const Quantity &other) const
Definition ffi_core.hpp:193
Quantity abs() const
Definition ffi_core.hpp:288
bool operator==(const Quantity &other) const
Definition ffi_core.hpp:229
constexpr double value() const
Definition ffi_core.hpp:152
Quantity operator*(double scalar) const
Definition ffi_core.hpp:208
Quantity operator-(const Quantity &other) const
Definition ffi_core.hpp:197
Quantity operator-() const
Definition ffi_core.hpp:284
Quantity & operator+=(const Quantity &other)
Definition ffi_core.hpp:259
Quantity & operator-=(const Quantity &other)
Definition ffi_core.hpp:264
Quantity & operator/=(double scalar)
Definition ffi_core.hpp:274
bool operator>(const Quantity &other) const
Definition ffi_core.hpp:241
Quantity operator/(double scalar) const
Definition ffi_core.hpp:212
static constexpr UnitId unit_id()
Definition ffi_core.hpp:147
friend Quantity operator*(double scalar, const Quantity &q)
Definition ffi_core.hpp:217
constexpr Quantity(double value)
Definition ffi_core.hpp:144
UnitTag unit_tag
Definition ffi_core.hpp:140
bool operator>=(const Quantity &other) const
Definition ffi_core.hpp:249
Quantity< typename ExtractTag< TargetType >::type > to() const
Definition ffi_core.hpp:171
void check_status(int32_t status, const char *operation)
Convert qtty FFI status codes into typed C++ exceptions.
Definition ffi_core.hpp:72