Last active
June 16, 2018 17:17
-
-
Save notfoundry/387d70ba35f60cdc96d2a82e27b8373f to your computer and use it in GitHub Desktop.
Strongly-dimensioned quantity types for C++11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <cstddef> | |
| #include <type_traits> | |
| #include <boost/mp11/list.hpp> | |
| #include <boost/mp11/bind.hpp> | |
| #include <boost/mp11/utility.hpp> | |
| #include <boost/mp11/function.hpp> | |
| #include <boost/mp11/integral.hpp> | |
| #include <boost/mp11/algorithm.hpp> | |
| #include <boost/mp11/map.hpp> | |
| namespace sdim { | |
| template <typename ExpUnitList, typename Value = double> | |
| struct val; | |
| template <typename FromUnit, typename ToUnit> | |
| struct unit_conversion; | |
| #ifdef STRONGDIM_ENFORCE_CORRECTNESS | |
| namespace detail { | |
| struct test_unit_1 {}; | |
| struct test_unit_2 {}; | |
| struct test_unit_3 {}; | |
| struct test_unit_4 {}; | |
| struct test_unit_5 {}; | |
| } | |
| template <> | |
| struct unit_conversion<detail::test_unit_1, detail::test_unit_2> { | |
| constexpr static auto conversion_factor = 2; | |
| }; | |
| template <> | |
| struct unit_conversion<detail::test_unit_3, detail::test_unit_4> { | |
| constexpr static auto conversion_factor = 3; | |
| }; | |
| #endif //STRONGDIM_ENFORCE_CORRECTNESS | |
| namespace detail { | |
| using namespace boost::mp11; | |
| template <bool Cond, template <typename...> class DeferredCond, typename... Args> | |
| struct lazy_enable_if { | |
| using type = void; | |
| }; | |
| template <template <typename...> class DeferredCond, typename... Args> | |
| struct lazy_enable_if<false, DeferredCond, Args...> { | |
| using type = typename std::enable_if<DeferredCond<Args...>::value>::type; | |
| }; | |
| template <typename Unit, int Exponent> | |
| struct exp_unit { | |
| using unit_type = Unit; | |
| constexpr static int exponent = Exponent; | |
| }; | |
| template <typename T> | |
| struct is_exp_unit : std::false_type {}; | |
| template <typename Unit, int Exponent> | |
| struct is_exp_unit<exp_unit<Unit, Exponent>> : std::true_type {}; | |
| template <typename UnitList> | |
| struct wrap_units_as_exp_units_if_present { | |
| private: | |
| template <typename Unit> | |
| using wrap_unit_if_necessary = mp_if< | |
| mp_or<is_exp_unit<Unit>, mp_is_list<Unit>>, | |
| Unit, | |
| exp_unit<Unit, 1> | |
| >; | |
| template <typename Accumulator, typename Unit> | |
| using update_accumulator = mp_push_back<Accumulator, wrap_unit_if_necessary<Unit>>; | |
| public: | |
| using type = mp_fold<UnitList, mp_list<>, update_accumulator>; | |
| }; | |
| #ifdef STRONGDIM_ENFORCE_CORRECTNESS | |
| static_assert( | |
| std::is_same< | |
| typename wrap_units_as_exp_units_if_present<mp_list<>>::type, | |
| mp_list<> | |
| >::value, | |
| "metafunction wrap_units_as_exp_units_if_present failed contract test"); | |
| static_assert( | |
| std::is_same< | |
| typename wrap_units_as_exp_units_if_present<mp_list< | |
| test_unit_1, test_unit_2, exp_unit<test_unit_3, 2> | |
| >>::type, | |
| mp_list<exp_unit<test_unit_1, 1>, exp_unit<test_unit_2, 1>, exp_unit<test_unit_3, 2>> | |
| >::value, | |
| "metafunction wrap_units_as_exp_units_if_present failed contract test"); | |
| #endif //STRONGDIM_ENFORCE_CORRECTNESS | |
| template <typename UnitList> | |
| struct consolidate_exp_unit_exponents_if_units_duplicated { | |
| private: | |
| template <typename MapUnit, typename MapExponent, typename CurrentExponent> | |
| using sum_exponents = mp_plus<MapExponent, CurrentExponent>; | |
| template <typename Map, typename Unit, typename Exponent> | |
| using update_map_impl = mp_map_update_q<Map, mp_list<Unit, Exponent>, mp_bind_back<sum_exponents, Exponent>>; | |
| template <typename Map, typename ExpUnit> | |
| using update_map = update_map_impl<Map, typename ExpUnit::unit_type, mp_int<ExpUnit::exponent>>; | |
| using unit_to_accumulated_exponent_map = mp_fold<UnitList, mp_list<>, update_map>; | |
| template <typename UnitAndExponentPair> | |
| using is_exponent_zero = mp_bool<mp_second<UnitAndExponentPair>::value == 0>; | |
| using unit_to_accumulated_exponent_map_after_removing_cancelled_units = mp_remove_if<unit_to_accumulated_exponent_map, is_exponent_zero>; | |
| template <typename UnitAndExponentPair> | |
| using unpack_unit_and_exponent = exp_unit<mp_first<UnitAndExponentPair>, mp_second<UnitAndExponentPair>::value>; | |
| public: | |
| using type = mp_transform<unpack_unit_and_exponent, unit_to_accumulated_exponent_map_after_removing_cancelled_units>; | |
| }; | |
| #ifdef STRONGDIM_ENFORCE_CORRECTNESS | |
| static_assert( | |
| std::is_same< | |
| typename consolidate_exp_unit_exponents_if_units_duplicated<mp_list< | |
| >>::type, | |
| mp_list<> | |
| >::value, | |
| "metafunction consolidate_exp_unit_exponents_if_units_duplicated failed contract test"); | |
| static_assert( | |
| std::is_same< | |
| typename consolidate_exp_unit_exponents_if_units_duplicated<mp_list< | |
| exp_unit<test_unit_2, 2>, exp_unit<test_unit_1, 2>, exp_unit<test_unit_1, 1>, exp_unit<test_unit_3, 4> | |
| >>::type, | |
| mp_list<exp_unit<test_unit_2, 2>, exp_unit<test_unit_1, 3>, exp_unit<test_unit_3, 4>> | |
| >::value, | |
| "metafunction consolidate_exp_unit_exponents_if_units_duplicated failed contract test"); | |
| #endif //STRONGDIM_ENFORCE_CORRECTNESS | |
| template <typename TypeOrTypeListList, typename Accumulator = mp_list<>, bool Done = mp_empty<TypeOrTypeListList>::value> | |
| struct flatten; | |
| template <typename TypeOrTypeList, typename Accumulator> | |
| struct update_flattening_accumulator; | |
| template < | |
| template <typename...> class ListForTypes, typename... Types, | |
| template <typename...> class ListForAccumulator, typename... AccumulatedTypes | |
| > | |
| struct update_flattening_accumulator<ListForTypes<Types...>, ListForAccumulator<AccumulatedTypes...>> { | |
| using inner_type_list = ListForTypes<Types...>; | |
| using inner_flattening_result = typename flatten<inner_type_list>::type; | |
| using type = mp_append<ListForAccumulator<AccumulatedTypes...>, inner_flattening_result>; | |
| }; | |
| template <typename Type, template <typename...> class ListForAccumulator, typename... AccumulatedTypes> | |
| struct update_flattening_accumulator<Type, ListForAccumulator<AccumulatedTypes...>> { | |
| using type = ListForAccumulator<AccumulatedTypes..., Type>; | |
| }; | |
| template <typename TypeOrTypeListList, typename Accumulator, bool Done> | |
| struct flatten { | |
| using current_type = mp_front<TypeOrTypeListList>; | |
| using updated_accumulator = typename update_flattening_accumulator<current_type, Accumulator>::type; | |
| using next_type_or_type_list_list_step = mp_pop_front<TypeOrTypeListList>; | |
| using type = typename flatten<next_type_or_type_list_list_step, updated_accumulator>::type; | |
| }; | |
| template <typename TypeOrTypeListList, typename Accumulator> | |
| struct flatten<TypeOrTypeListList, Accumulator, true> { | |
| using type = Accumulator; | |
| }; | |
| #ifdef STRONGDIM_ENFORCE_CORRECTNESS | |
| static_assert( | |
| std::is_same< | |
| typename flatten<mp_list<>>::type, | |
| mp_list<> | |
| >::value, | |
| "metafunction flatten failed contract test"); | |
| static_assert( | |
| std::is_same< | |
| typename flatten<mp_list< | |
| void*, | |
| mp_list<int, long, mp_list<char, short>>, | |
| int* | |
| >>::type, | |
| mp_list<void*, int, long, char, short, int*> | |
| >::value, | |
| "metafunction flatten failed contract test"); | |
| #endif //STRONGDIM_ENFORCE_CORRECTNESS | |
| template <typename UnitList> | |
| struct normalize_unit_list { | |
| private: | |
| using flattended_unit_list = typename flatten<UnitList>::type; | |
| using wrapped_exp_unit_list = typename wrap_units_as_exp_units_if_present<flattended_unit_list>::type; | |
| using consolidated_exp_unit_list = typename consolidate_exp_unit_exponents_if_units_duplicated<wrapped_exp_unit_list>::type; | |
| public: | |
| using type = consolidated_exp_unit_list; | |
| }; | |
| template < | |
| typename ThisUnitList, typename ThatUnitList, | |
| bool SameSize = (mp_size<ThisUnitList>::value == mp_size<ThatUnitList>::value) | |
| > struct are_list_contents_equal { | |
| private: | |
| template <typename Accumulator, typename Unit> | |
| using this_list_contains = mp_push_back<Accumulator, mp_contains<ThisUnitList, Unit>>; | |
| using unit_contains_check_results = mp_fold<ThatUnitList, mp_list<>, this_list_contains>; | |
| public: | |
| constexpr static bool value = mp_not<mp_contains<unit_contains_check_results, mp_false>>::value; | |
| }; | |
| template <typename ThisUnitList, typename ThatUnitList> | |
| struct are_list_contents_equal<ThisUnitList, ThatUnitList, false> { | |
| constexpr static bool value = false; | |
| }; | |
| template <typename FirstUnit, typename SecondUnit> | |
| struct are_units_equivalent { | |
| private: | |
| using normalized_first_exp_units = typename normalize_unit_list<mp_list<FirstUnit>>::type; | |
| using normalized_second_exp_units = typename normalize_unit_list<mp_list<SecondUnit>>::type; | |
| public: | |
| constexpr static bool value = are_list_contents_equal<normalized_first_exp_units, normalized_second_exp_units>::value; | |
| }; | |
| template <typename T> | |
| struct is_val : std::false_type {}; | |
| template <typename ExpUnitList, typename Value> | |
| struct is_val<val<ExpUnitList, Value>> : std::true_type {}; | |
| template <typename OtherVal, typename ExpUnitListToCompare, bool IsVal = is_val<OtherVal>::value> | |
| struct is_equivalent_val { | |
| constexpr static bool value = are_units_equivalent<ExpUnitListToCompare, typename OtherVal::unit_list_type>::value; | |
| }; | |
| template <typename OtherVal, typename ExpUnitListToCompare> | |
| struct is_equivalent_val<OtherVal, ExpUnitListToCompare, false> : std::false_type {}; | |
| template <typename... Units> | |
| struct take_unit_list_product { | |
| private: | |
| using unit_list = mp_list<Units...>; | |
| using flattened_and_normalized_exp_unit_list = typename normalize_unit_list<unit_list>::type; | |
| public: | |
| using type = flattened_and_normalized_exp_unit_list; | |
| }; | |
| template <typename ExpUnitList, template <typename, int> class F> | |
| struct apply_to_exp_units { | |
| private: | |
| template <typename ExpUnit> | |
| using apply_function = F<typename ExpUnit::unit_type, ExpUnit::exponent>; | |
| public: | |
| using type = mp_transform<apply_function, ExpUnitList>; | |
| }; | |
| template <typename OfUnit, typename ToUnit> | |
| struct take_unit_ratio { | |
| private: | |
| using normalized_numerator_exp_units = typename normalize_unit_list<mp_list<OfUnit>>::type; | |
| using normalized_denominator_exp_units = typename normalize_unit_list<mp_list<ToUnit>>::type; | |
| template <typename Unit, int Exponent> | |
| using invert_exponent_sign = exp_unit<Unit, -Exponent>; | |
| using exp_inverted_denominator_units = typename apply_to_exp_units<normalized_denominator_exp_units, invert_exponent_sign>::type; | |
| using concatenated_exp_unit_list = mp_append<normalized_numerator_exp_units, exp_inverted_denominator_units>; | |
| using consolidated_exp_unit_list = typename consolidate_exp_unit_exponents_if_units_duplicated<concatenated_exp_unit_list>::type; | |
| public: | |
| using type = consolidated_exp_unit_list; | |
| }; | |
| template <typename Unit, int Exponent> | |
| struct take_unit_power { | |
| private: | |
| using normalized_exp_units = typename normalize_unit_list<mp_list<Unit>>::type; | |
| template <typename InnerUnit, int InnerUnitExponent> | |
| using multiply_unit_exponent_with_chosen_exponent = exp_unit<InnerUnit, InnerUnitExponent * Exponent>; | |
| public: | |
| using type = typename apply_to_exp_units<normalized_exp_units, multiply_unit_exponent_with_chosen_exponent>::type; | |
| }; | |
| template <typename UnitConversion> | |
| struct inverse_conversion { | |
| using unit_conversion = UnitConversion; | |
| }; | |
| template <typename UnitConversion> | |
| struct unit_conversion_to_conversion_function { | |
| template <typename Value> | |
| constexpr static Value convert(Value value) { | |
| return value * UnitConversion::conversion_factor; | |
| } | |
| }; | |
| template <typename UnitConversion> | |
| struct unit_conversion_to_conversion_function<inverse_conversion<UnitConversion>> { | |
| template <typename Value> | |
| constexpr static Value convert(Value value) { | |
| return value / UnitConversion::conversion_factor; | |
| } | |
| }; | |
| template <typename GivenUnitSet, typename RequiredUnitSet, bool SameSize = (mp_size<GivenUnitSet>::value == mp_size<RequiredUnitSet>::value)> | |
| struct is_valid_unit_conversion_possible { | |
| template <typename MapExponent, typename MapUnitCount> | |
| using increment_unit_count_for_exponent = mp_int<MapUnitCount::value + 1>; | |
| template <typename ExponentToUnitCountMap, typename ExpUnit> | |
| using count_units_with_same_exponent = mp_map_update< | |
| ExponentToUnitCountMap, | |
| mp_list<mp_int<ExpUnit::exponent>, mp_int<1>>, | |
| increment_unit_count_for_exponent | |
| >; | |
| using exponent_to_unit_count_map = mp_fold<mp_append<GivenUnitSet, RequiredUnitSet>, mp_list<>, count_units_with_same_exponent>; | |
| constexpr static int units_to_find_if_conversion_possible = 2; | |
| using unit_counts = mp_transform<mp_second, exponent_to_unit_count_map>; | |
| template <typename I> | |
| using is_power_of_two = mp_bool<I::value % 2 == 0>; | |
| using unpaired_units_count = mp_size<mp_remove_if<unit_counts, is_power_of_two>>; | |
| constexpr static bool value = (unpaired_units_count::value == 0); | |
| }; | |
| template <typename GivenUnitSet, typename RequiredUnitSet> | |
| struct is_valid_unit_conversion_possible<GivenUnitSet, RequiredUnitSet, false> { | |
| constexpr static bool value = false; | |
| }; | |
| #ifdef STRONGDIM_ENFORCE_CORRECTNESS | |
| static_assert( | |
| is_valid_unit_conversion_possible< | |
| mp_list<>, | |
| mp_list<> | |
| >::value, | |
| "metafunction is_valid_unit_conversion_possible failed contract test"); | |
| static_assert( | |
| is_valid_unit_conversion_possible< | |
| mp_list<exp_unit<test_unit_1, 2>, exp_unit<test_unit_3, 1>, exp_unit<test_unit_2, 2>>, | |
| mp_list<exp_unit<test_unit_1, 1>, exp_unit<test_unit_2, 2>, exp_unit<test_unit_3, 2>> | |
| >::value, | |
| "metafunction is_valid_unit_conversion_possible failed contract test"); | |
| static_assert( | |
| !is_valid_unit_conversion_possible< | |
| mp_list<exp_unit<test_unit_1, 4>, exp_unit<test_unit_3, 1>, exp_unit<test_unit_2, 2>>, | |
| mp_list<exp_unit<test_unit_1, 1>, exp_unit<test_unit_2, 2>, exp_unit<test_unit_3, 2>> | |
| >::value, | |
| "metafunction is_valid_unit_conversion_possible failed contract test"); | |
| #endif //STRONGDIM_ENFORCE_CORRECTNESS | |
| template <typename FirstConversionFunction, typename SecondConversionFunction> | |
| struct apply_both_conversion_functions { | |
| template <typename Value> | |
| constexpr static Value convert(Value value) { | |
| return SecondConversionFunction::convert(FirstConversionFunction::convert(value)); | |
| } | |
| }; | |
| struct identity_conversion_function { | |
| template <typename Value> | |
| constexpr static Value convert(Value value) { | |
| return value; | |
| } | |
| }; | |
| template <typename ConversionFunctionList> | |
| struct sequental_conversion_function_applier { | |
| private: | |
| template <typename Accumulator, typename ConversionFunction> | |
| using make_seq_application = apply_both_conversion_functions<Accumulator, ConversionFunction>; | |
| using sequential_converter = mp_fold<mp_pop_front<ConversionFunctionList>, mp_front<ConversionFunctionList>, make_seq_application>; | |
| public: | |
| template <typename Value> | |
| constexpr static Value convert(Value value) { | |
| return sequential_converter::convert(value); | |
| } | |
| }; | |
| template <typename InList, typename FromList> | |
| struct remove_all { | |
| private: | |
| template <typename Accumulator, typename T> | |
| using do_removal = mp_remove<Accumulator, T>; | |
| public: | |
| using type = mp_fold<InList, FromList, do_removal>; | |
| }; | |
| template < | |
| typename GivenUnitSet, typename RequiredUnitSet, | |
| bool ConversionPossible = is_valid_unit_conversion_possible<GivenUnitSet, RequiredUnitSet>::value | |
| > | |
| struct try_make_conversion_function { | |
| private: | |
| static_assert( | |
| mp_size<GivenUnitSet>::value == mp_size<RequiredUnitSet>::value, | |
| "given and required unit sets must be the same size"); | |
| using unfulfilled_required_exp_unit_set = typename remove_all<GivenUnitSet, RequiredUnitSet>::type; | |
| using unused_given_exp_unit_set = typename remove_all<RequiredUnitSet, GivenUnitSet>::type; | |
| template <typename ExpUnit> | |
| using to_unit = typename ExpUnit::unit_type; | |
| using unfulfilled_required_unit_set = mp_transform<to_unit, unfulfilled_required_exp_unit_set>; | |
| using unused_given_unit_set = mp_transform<to_unit, unused_given_exp_unit_set>; | |
| template < | |
| typename RemainingUnusedGivenUnitSet, typename FoundUnitConverterSet, | |
| typename GivenUnit, typename UnitConverter | |
| > | |
| using update_inner_reduction_accumulators_impl = mp_list< | |
| mp_remove<RemainingUnusedGivenUnitSet, GivenUnit>, | |
| mp_push_back<FoundUnitConverterSet, unit_conversion_to_conversion_function<UnitConverter>> | |
| >; | |
| template <typename Accumulators, typename GivenUnit, typename DeferredUnitConverter> | |
| using update_inner_reduction_accumulators = update_inner_reduction_accumulators_impl< | |
| mp_first<Accumulators>, mp_second<Accumulators>, GivenUnit, typename DeferredUnitConverter::type | |
| >; | |
| template <typename GivenUnit, typename RequiredUnit> | |
| using make_unit_conversion = unit_conversion<GivenUnit, RequiredUnit>; | |
| template <typename GivenUnit, typename RequiredUnit> | |
| using make_inverse_unit_conversion = inverse_conversion<make_unit_conversion<RequiredUnit, GivenUnit>>; | |
| template <typename GivenUnit, typename RequiredUnit> | |
| using get_unit_conversion_factor_type = decltype(unit_conversion<GivenUnit, RequiredUnit>::conversion_factor); | |
| template <typename Accumulators, typename GivenUnit, typename RequiredUnit> | |
| using do_inner_reduction_for_inverse_conversion = mp_eval_if< | |
| mp_not<mp_valid<get_unit_conversion_factor_type, RequiredUnit, GivenUnit>>, | |
| Accumulators, | |
| update_inner_reduction_accumulators, | |
| Accumulators, | |
| GivenUnit, | |
| mp_defer< | |
| make_inverse_unit_conversion, | |
| GivenUnit, | |
| RequiredUnit | |
| > | |
| >; | |
| template <typename Accumulators, typename GivenUnit, typename RequiredUnit> | |
| using do_inner_reduction_for_direct_conversion = mp_eval_if< | |
| mp_not<mp_valid<get_unit_conversion_factor_type, GivenUnit, RequiredUnit>>, | |
| do_inner_reduction_for_inverse_conversion<Accumulators, GivenUnit, RequiredUnit>, | |
| update_inner_reduction_accumulators, | |
| Accumulators, | |
| GivenUnit, | |
| mp_defer< | |
| make_unit_conversion, | |
| GivenUnit, | |
| RequiredUnit | |
| > | |
| >; | |
| template <typename RemainingUnusedGivenUnitSet, typename FoundUnitConverterSet, typename RequiredUnit> | |
| using inner_reduction = mp_fold_q< | |
| RemainingUnusedGivenUnitSet, | |
| mp_list<RemainingUnusedGivenUnitSet, FoundUnitConverterSet>, | |
| mp_bind_back<do_inner_reduction_for_direct_conversion, RequiredUnit> | |
| >; | |
| template <typename Accumulators, typename RequiredUnit> | |
| using do_outer_reduction = inner_reduction<mp_first<Accumulators>, mp_second<Accumulators>, RequiredUnit>; | |
| using outer_reduction = mp_fold< | |
| unfulfilled_required_unit_set, | |
| mp_list<unused_given_unit_set, mp_list<>>, | |
| do_outer_reduction | |
| >; | |
| using unit_conversion_function_sequence = mp_second<outer_reduction>; | |
| using conversion_failed = mp_bool<mp_size<unit_conversion_function_sequence>::value != mp_size<unfulfilled_required_unit_set>::value>; | |
| using final_conversion_function = mp_eval_if< | |
| conversion_failed, | |
| void, | |
| sequental_conversion_function_applier, | |
| unit_conversion_function_sequence | |
| >; | |
| public: | |
| using type = final_conversion_function; | |
| constexpr static bool could_convert = !conversion_failed::value; | |
| }; | |
| template <typename GivenUnitSet, typename RequiredUnitSet> | |
| struct try_make_conversion_function<GivenUnitSet, RequiredUnitSet, false> { | |
| using type = void; | |
| constexpr static bool could_convert = false; | |
| }; | |
| #ifdef STRONGDIM_ENFORCE_CORRECTNESS | |
| static_assert( | |
| try_make_conversion_function< | |
| mp_list<exp_unit<test_unit_1, 1>, exp_unit<test_unit_3, 1>, exp_unit<test_unit_5, 1>>, | |
| mp_list<exp_unit<test_unit_5, 1>, exp_unit<test_unit_2, 1>, exp_unit<test_unit_4, 1>> | |
| >::could_convert, | |
| "metafunction try_make_conversion_function failed contract test"); | |
| static_assert( | |
| try_make_conversion_function< | |
| mp_list<exp_unit<test_unit_1, 1>, exp_unit<test_unit_3, 1>, exp_unit<test_unit_5, 1>>, | |
| mp_list<exp_unit<test_unit_5, 1>, exp_unit<test_unit_2, 1>, exp_unit<test_unit_4, 1>> | |
| >::type::convert(2) == 2 * unit_conversion<test_unit_1, test_unit_2>::conversion_factor * unit_conversion<test_unit_3, test_unit_4>::conversion_factor, | |
| "metafunction try_make_conversion_function failed contract test"); | |
| static_assert( | |
| !try_make_conversion_function< | |
| mp_list<exp_unit<test_unit_5, 1>, exp_unit<test_unit_1, 1>>, | |
| mp_list<exp_unit<test_unit_2, 1>, exp_unit<test_unit_4, 1>> | |
| >::could_convert, | |
| "metafunction try_make_conversion_function failed contract test"); | |
| #endif //STRONGDIM_ENFORCE_CORRECTNESS | |
| template <typename GivenUnitSet, typename RequiredUnitSet, bool UnitsEquivalent = are_list_contents_equal<GivenUnitSet, RequiredUnitSet>::value> | |
| struct find_unit_conversion { | |
| using type = identity_conversion_function; | |
| constexpr static bool could_convert = true; | |
| }; | |
| template <typename GivenUnitSet, typename RequiredUnitSet> | |
| struct find_unit_conversion<GivenUnitSet, RequiredUnitSet, false> : try_make_conversion_function<GivenUnitSet, RequiredUnitSet> {}; | |
| } | |
| template <typename... Units> | |
| using product = typename detail::take_unit_list_product<Units...>::type; | |
| template <typename OfUnit, typename ToUnit> | |
| using rate = typename detail::take_unit_ratio<OfUnit, ToUnit>::type; | |
| template <typename Unit, int Exponent> | |
| using power = typename detail::take_unit_power<Unit, Exponent>::type; | |
| template <typename ExpUnitList, typename Value> | |
| struct val { | |
| public: | |
| using unit_list_type = typename detail::normalize_unit_list<boost::mp11::mp_list<ExpUnitList>>::type; | |
| using value_type = Value; | |
| private: | |
| template <typename OtherNum> | |
| using value_type_when_mult_with_num = decltype(std::declval<value_type>() * std::declval<OtherNum>()); | |
| template <typename OtherVal> | |
| using value_type_when_mult_with_val = value_type_when_mult_with_num<typename OtherVal::value_type>; | |
| template <typename OtherNum> | |
| using value_type_when_div_by_num = decltype(std::declval<value_type>() * std::declval<OtherNum>()); | |
| template <typename OtherVal> | |
| using value_type_when_div_by_val = value_type_when_div_by_num<typename OtherVal::value_type>; | |
| template <typename OtherNum> | |
| using multiply_by_num = val<unit_list_type, value_type_when_mult_with_num<OtherNum>>; | |
| template <typename OtherVal> | |
| using multiply_by_val = val< | |
| product<unit_list_type, typename OtherVal::unit_list_type>, | |
| value_type_when_mult_with_val<OtherVal> | |
| >; | |
| template <typename OtherNum> | |
| using divide_by_num = val<unit_list_type, value_type_when_div_by_num<OtherNum>>; | |
| template <typename OtherVal> | |
| using divide_by_val = val< | |
| rate<unit_list_type, typename OtherVal::unit_list_type>, | |
| value_type_when_div_by_val<OtherVal> | |
| >; | |
| public: | |
| constexpr val(value_type value) : value(value) {} | |
| template < | |
| typename OtherVal, | |
| typename Converter = detail::find_unit_conversion<typename OtherVal::unit_list_type, unit_list_type>, | |
| typename std::enable_if<Converter::could_convert>::type* = nullptr | |
| > | |
| constexpr val(const OtherVal& other) : value(Converter::type::convert(other.value)) {} | |
| template <typename OtherVal, typename std::enable_if<detail::is_val<OtherVal>::value>::type* = nullptr> | |
| constexpr auto operator*(const OtherVal& other) const -> multiply_by_val<OtherVal> { | |
| return multiply_by_val<OtherVal>(this->value * other.value); | |
| } | |
| template <typename OtherNum, typename std::enable_if<std::is_arithmetic<OtherNum>::value>::type* = nullptr> | |
| constexpr auto operator*(const OtherNum& other) const -> multiply_by_num<OtherNum> { | |
| return val<unit_list_type>(this->value * other); | |
| } | |
| template <typename OtherVal, typename std::enable_if<detail::is_val<OtherVal>::value>::type* = nullptr> | |
| constexpr auto operator/(const OtherVal& other) const -> divide_by_val<OtherVal> { | |
| return divide_by_val<OtherVal>(this->value / other.value); | |
| } | |
| template <typename OtherNum, typename std::enable_if<std::is_arithmetic<OtherNum>::value>::type* = nullptr> | |
| constexpr auto operator/(const OtherNum& other) const -> divide_by_num<OtherNum> { | |
| return divide_by_num<OtherNum>(this->value / other); | |
| } | |
| value_type value; | |
| }; | |
| } | |
| struct seconds {}; | |
| struct meters {}; | |
| struct kilograms {}; | |
| struct feet {}; | |
| namespace sdim { | |
| template <> | |
| struct unit_conversion<meters, feet> { | |
| constexpr static auto conversion_factor = 3.280839895; | |
| }; | |
| } | |
| int main() { | |
| using namespace sdim; | |
| val<seconds> time(10); | |
| val<meters> distance(5); | |
| val<kilograms> mass(100); | |
| val<rate<meters, seconds>> speed = distance / time; | |
| val<rate<meters, power<seconds, 2>>> acceleration = speed / time; | |
| val<product<kilograms, rate<power<meters, 2>, power<seconds, 2>>>> energy = mass * distance * acceleration; | |
| val<product<power<meters, 2>, power<seconds, -2>, power<kilograms, 1>>> alt_energy = energy; | |
| //val<product<power<meters, 1>, power<seconds, -2>, power<kilograms, 1>>> will_not_compile = energy; | |
| val<feet> distance_ft (25); | |
| val<meters> distance_m = distance_m; | |
| val<meters> times_scalar = distance_m * 2; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment