|
| 1 | +#include <iostream> |
| 2 | +#include <type_traits> |
| 3 | +#include <tuple> |
| 4 | +#include <cxxabi.h> |
| 5 | +#include <typeinfo> |
| 6 | +#include <memory> |
| 7 | +#include <cstdlib> |
| 8 | +#include <string> |
| 9 | + |
| 10 | +// Base unit struct for distinguishing different units |
| 11 | +struct BaseUnit {}; |
| 12 | + |
| 13 | +template<typename... Units> |
| 14 | +struct Unit { |
| 15 | + // Static assert to check all Units are derived from BaseUnit |
| 16 | + static_assert(std::conjunction<std::is_base_of<BaseUnit, Units>...>::value, |
| 17 | + "All units in Unit must be subclasses of BaseUnit"); |
| 18 | +}; |
| 19 | + |
| 20 | +// Specific physical quantity tags |
| 21 | +struct Length : BaseUnit {}; |
| 22 | +struct Mass : BaseUnit {}; |
| 23 | +struct Time : BaseUnit {}; |
| 24 | + |
| 25 | +// Template to represent inverse units |
| 26 | +template<typename U> |
| 27 | +struct InverseUnit : BaseUnit |
| 28 | +{ |
| 29 | + static_assert(std::is_base_of<BaseUnit, U>::value, "U must be a subclass of BaseUnit"); |
| 30 | +}; |
| 31 | + |
| 32 | +template<typename U> |
| 33 | +struct Invert; |
| 34 | + |
| 35 | +// Example of a compound unit: Length*Time^-1 |
| 36 | +using VelocityUnit = Unit<Length, InverseUnit<Time>>; |
| 37 | + |
| 38 | +template<typename U1, typename U2> |
| 39 | +struct CanMultiply : std::true_type {}; |
| 40 | + |
| 41 | +template<typename U1, typename U2> |
| 42 | +struct CanDivide : std::true_type {}; |
| 43 | + |
| 44 | +/// Unit Simplification Logic |
| 45 | +template <typename> |
| 46 | +struct SimplifyUnit; |
| 47 | + |
| 48 | +template<> |
| 49 | +struct SimplifyUnit<Unit<>> { |
| 50 | + using type = Unit<>; |
| 51 | +}; |
| 52 | + |
| 53 | + |
| 54 | +// Type trait to detect inverse units |
| 55 | +template<typename U> |
| 56 | +struct IsInverseUnit : std::false_type {}; |
| 57 | + |
| 58 | +template<typename U> |
| 59 | +struct IsInverseUnit<InverseUnit<U>> : std::true_type {}; |
| 60 | + |
| 61 | + |
| 62 | + |
| 63 | + |
| 64 | +template<typename List, typename U> |
| 65 | +struct AppendUnit; |
| 66 | + |
| 67 | +template<typename... Units, typename U> |
| 68 | +struct AppendUnit<Unit<Units...>, U> { |
| 69 | + using type = Unit<Units..., U>; |
| 70 | +}; |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | +// Base template for a single unit, enabled only when U is not an InverseUnit |
| 75 | +template<typename U> |
| 76 | +struct Invert { |
| 77 | + using type = InverseUnit<U>; |
| 78 | +}; |
| 79 | + |
| 80 | +// Specialization for an InverseUnit to strip one layer of inversion |
| 81 | +template<typename U> |
| 82 | +struct Invert<InverseUnit<U>> { |
| 83 | + using type = U; // Simplifies the double inversion |
| 84 | +}; |
| 85 | + |
| 86 | +// Helper to invert a unit pack |
| 87 | +template<typename... Units> |
| 88 | +struct InvertUnitPack; |
| 89 | + |
| 90 | +template<typename U, typename... Us> |
| 91 | +struct InvertUnitPack<U, Us...> { |
| 92 | + using type = typename AppendUnit<typename InvertUnitPack<Us...>::type, typename Invert<U>::type>::type; |
| 93 | +}; |
| 94 | + |
| 95 | +template<> |
| 96 | +struct InvertUnitPack<> { |
| 97 | + using type = Unit<>; |
| 98 | +}; |
| 99 | + |
| 100 | + |
| 101 | +template<typename List, typename U> |
| 102 | +struct RemoveUnit; |
| 103 | + |
| 104 | +// Base case: When the list is empty |
| 105 | +template<typename U> |
| 106 | +struct RemoveUnit<Unit<>, U> { |
| 107 | + using type = Unit<>; |
| 108 | +}; |
| 109 | + |
| 110 | +// Recursive case: when the first type matches the type to remove |
| 111 | +template<typename First, typename... Rest> |
| 112 | +struct RemoveUnit<Unit<First, Rest...>, First> { |
| 113 | + using type = Unit<Rest...>; // Remove First and return the rest |
| 114 | +}; |
| 115 | + |
| 116 | +// Recursive case: when the first type does not match |
| 117 | +template<typename First, typename... Rest, typename U> |
| 118 | +struct RemoveUnit<Unit<First, Rest...>, U> { |
| 119 | + using type = typename AppendUnit<typename RemoveUnit<Unit<Rest...>, U>::type, First>::type; |
| 120 | +}; |
| 121 | + |
| 122 | +static_assert(std::is_same<typename RemoveUnit<Unit<Time>, Time>::type, Unit<>>::value, "Unit<Time> should be removed, leaving an empty list."); |
| 123 | + |
| 124 | +template<typename List, typename T> |
| 125 | +struct CountType; |
| 126 | + |
| 127 | +template<typename T> |
| 128 | +struct CountType<Unit<>, T> : std::integral_constant<int, 0> {}; |
| 129 | + |
| 130 | +// Recursive case: Increment count if First is the same type as T, then continue checking the rest. |
| 131 | +template<typename First, typename... Rest, typename T> |
| 132 | +struct CountType<Unit<First, Rest...>, T> |
| 133 | + : std::integral_constant<int, std::is_same<Unit<First>, Unit<T>>::value + CountType<Unit<Rest...>, T>::value> {}; |
| 134 | + |
| 135 | + |
| 136 | + |
| 137 | +template<typename Unit1, typename Unit2> |
| 138 | +struct Multiply; |
| 139 | + |
| 140 | +template<typename... Units1, typename... Units2> |
| 141 | +struct Multiply<Unit<Units1...>, Unit<Units2...>> { |
| 142 | + using type = typename SimplifyUnit<Unit<Units1..., Units2...>>::type; |
| 143 | +}; |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | +template<typename Unit1, typename Unit2> |
| 148 | +struct Divide; |
| 149 | + |
| 150 | +// Modified Divide to use InvertUnitPack for the second unit pack |
| 151 | +template<typename... Units1, typename... Units2> |
| 152 | +struct Divide<Unit<Units1...>, Unit<Units2...>> { |
| 153 | + using InvertedUnits2 = typename InvertUnitPack<Units2...>::type; |
| 154 | + using type = typename Multiply<Unit<Units1...>, InvertedUnits2>::type; |
| 155 | +}; |
| 156 | + |
| 157 | + |
| 158 | + |
| 159 | +template< typename List > |
| 160 | +struct CancelUnits; |
| 161 | + |
| 162 | +template<> |
| 163 | +struct CancelUnits<Unit<>> { |
| 164 | + using type = Unit<>; |
| 165 | +}; |
| 166 | + |
| 167 | +template< typename U > |
| 168 | +struct CancelUnits<Unit<U>> { |
| 169 | + using type = Unit<U>; |
| 170 | +}; |
| 171 | + |
| 172 | +template<typename First, typename... Rest> |
| 173 | +struct CancelUnits<Unit<First, Rest...>> { |
| 174 | + using RestSimplified = typename CancelUnits<Unit<Rest...>>::type; |
| 175 | + // static_assert( std::is_same< RestSimplified, Unit<Time> >::value ); |
| 176 | + |
| 177 | + // Determine if the inverse of First exists in the simplified rest |
| 178 | + static constexpr bool hasInverse = CountType<RestSimplified, typename Invert<First>::type>::value > 0; |
| 179 | + // static_assert( CountType<RestSimplified, typename Invert<First>::type>::value == 1 ); |
| 180 | + // static_assert( hasInverse ); |
| 181 | + |
| 182 | + // static_assert( std::is_same< typename Invert<First>::type, Time >::value ); |
| 183 | + |
| 184 | + // Type used to remove the inverse of First if found |
| 185 | + using RestWithoutInverse = typename std::conditional< |
| 186 | + hasInverse, |
| 187 | + typename RemoveUnit<RestSimplified, typename Invert<First>::type>::type, |
| 188 | + RestSimplified |
| 189 | + >::type; |
| 190 | + // static_assert( std::is_same< typename RemoveUnit<RestSimplified, typename Invert<First>::type>::type, Unit<Time> >::value ); |
| 191 | + // static_assert( std::is_same< RestWithoutInverse, Unit<> >::value ); |
| 192 | + |
| 193 | + // Recurse on the potentially modified list after modification (removal of First and its inverse) |
| 194 | + using type = typename std::conditional< |
| 195 | + hasInverse, |
| 196 | + typename CancelUnits<RestWithoutInverse>::type, // Continue simplification without First and its inverse |
| 197 | + typename AppendUnit<RestWithoutInverse, First>::type // No inverse found, add First back to the list and continue |
| 198 | + >::type; |
| 199 | +}; |
| 200 | + |
| 201 | +template<typename... Units> |
| 202 | +struct SimplifyUnit<Unit<Units...>> { |
| 203 | + using type = typename CancelUnits<Unit<Units...>>::type; |
| 204 | +}; |
| 205 | + |
| 206 | + |
| 207 | +template<typename List1, typename List2> |
| 208 | +struct EquivalentUnits; |
| 209 | + |
| 210 | +template<typename... Types1, typename... Types2> |
| 211 | +struct EquivalentUnits<Unit<Types1...>, Unit<Types2...>> { |
| 212 | + static constexpr bool compare_each() { |
| 213 | + return (... && (CountType<Unit<Types1...>, Types2>::value == CountType<Unit<Types2...>, Types1>::value)); |
| 214 | + } |
| 215 | + |
| 216 | + static constexpr bool value = compare_each(); |
| 217 | +}; |
| 218 | + |
| 219 | + |
| 220 | +// template<typename T, typename U> |
| 221 | +// class Quantity { |
| 222 | +// public: |
| 223 | +// T value; |
| 224 | + |
| 225 | +// explicit Quantity(T v) : value(v) {} |
| 226 | + |
| 227 | +// operator T() const { |
| 228 | +// return value; |
| 229 | +// } |
| 230 | + |
| 231 | +// operator std::string() const { |
| 232 | +// return std::to_string(value); // Removed demangle for simplification |
| 233 | +// } |
| 234 | + |
| 235 | +// // Ensure units are the same for addition and subtraction |
| 236 | +// Quantity& operator+=(const Quantity& other) { |
| 237 | +// static_assert(std::is_same<U, U>::value, "Cannot add different units"); |
| 238 | +// value += other.value; |
| 239 | +// return *this; |
| 240 | +// } |
| 241 | + |
| 242 | +// Quantity& operator-=(const Quantity& other) { |
| 243 | +// static_assert(std::is_same<U, U>::value, "Cannot subtract different units"); |
| 244 | +// value -= other.value; |
| 245 | +// return *this; |
| 246 | +// } |
| 247 | + |
| 248 | +// Quantity operator+(const Quantity& other) const { |
| 249 | +// return Quantity(value + other.value); |
| 250 | +// } |
| 251 | + |
| 252 | +// Quantity operator-(const Quantity& other) const { |
| 253 | +// return Quantity(value - other.value); |
| 254 | +// } |
| 255 | + |
| 256 | +// // Multiplication (results in compound unit) |
| 257 | +// template<typename OU> |
| 258 | +// auto operator*(const Quantity<T, OU>& other) const { |
| 259 | +// static_assert(CanMultiply<U, OU>::value, "Cannot multiply these units"); |
| 260 | +// using ResultUnit = typename SimplifyUnit< typename Multiply< U, OU >::type >::type; |
| 261 | +// return Quantity<T, ResultUnit>(value * other.value); |
| 262 | +// } |
| 263 | + |
| 264 | +// // Division (results in unit ratio) |
| 265 | +// template<typename OU> |
| 266 | +// auto operator/(const Quantity<T, OU>& other) const { |
| 267 | +// static_assert(CanDivide<U, OU>::value, "Cannot divide these units"); |
| 268 | +// using ResultUnit = typename SimplifyUnit< typename Divide< U, OU >::type >::type; |
| 269 | +// return Quantity<T, Unit<>>(value / other.value); |
| 270 | +// } |
| 271 | +// }; |
| 272 | + |
| 273 | +// Quantity< double, Length > operator "" _L( long double length ) { return Quantity< double, Length >( length ); } |
| 274 | +// Quantity< double, Length > operator "" _L( unsigned long long length ) { return Quantity< double, Length >( length ); } |
| 275 | + |
| 276 | + |
| 277 | + |
| 278 | +template<typename T> |
| 279 | +void printTypeName() { |
| 280 | + const std::type_info& ti = typeid(T); |
| 281 | + int status = -1; |
| 282 | + std::unique_ptr<char, void(*)(void*)> demangled_name( |
| 283 | + abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status), std::free); |
| 284 | + std::cout << "The type is: " << (status == 0 ? demangled_name.get() : ti.name()) << std::endl; |
| 285 | +} |
0 commit comments