Support for derived dimensions in exp added

This commit is contained in:
Mateusz Pusz
2019-09-18 06:36:52 -06:00
parent 422c078cb2
commit 32a65a7405
14 changed files with 207 additions and 113 deletions

View File

@@ -74,8 +74,8 @@ constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto
### `Dimensions` ### `Dimensions`
`units::dimension` is a type-list like type that stores an ordered list of exponents of one `units::dimension` represents a derived dimension and is implemented as a type-list like type that
or more base dimensions: stores an ordered list of exponents of one or more base dimensions:
```cpp ```cpp
template<Exponent... Es> template<Exponent... Es>
@@ -94,28 +94,6 @@ concept Dimension =
#### `Exponents` #### `Exponents`
`units::exp` provides an information about a single base dimension and its (possibly fractional)
exponent in a derived dimension:
```cpp
template<const base_dimension& BaseDimension, int Num, int Den = 1>
struct exp {
static constexpr const base_dimension& dimension = BaseDimension;
static constexpr int num = Num;
static constexpr int den = Den;
};
```
where `BaseDimension` is a unique sortable compile-time value:
```cpp
struct base_dimension {
const char* name;
};
constexpr bool operator==(const base_dimension& lhs, const base_dimension& rhs);
constexpr bool operator<(const base_dimension& lhs, const base_dimension& rhs);
```
`units::Exponent` concept is satisfied if provided type is an instantiation of `units::exp` class `units::Exponent` concept is satisfied if provided type is an instantiation of `units::exp` class
template: template:
@@ -125,6 +103,45 @@ concept Exponent =
detail::is_exp<T>; // exposition only detail::is_exp<T>; // exposition only
``` ```
`units::exp` provides an information about a single dimension and its (possibly fractional)
exponent in a derived dimension.
```cpp
template<typename Dim, int Num, int Den = 1>
requires BaseDimension<Dim> || Dimension<Dim>
struct exp {
using dimension = Dim;
static constexpr int num = Num;
static constexpr int den = Den;
};
```
Both a base dimension and a derived dimension can be provided to `units::exp` class template.
`units::BaseDimension` represents a base dimension and should be implemented as a type with
a unique compile-time text describing the dimension name:
```cpp
template<typename T>
concept BaseDimension = std::is_empty_v<T> &&
requires {
{ T::value } -> std::same_as<const char*>;
};
```
For example here is a list of SI base dimensions:
```cpp
struct base_dim_length { static constexpr const char* value = "length"; };
struct base_dim_mass { static constexpr const char* value = "mass"; };
struct base_dim_time { static constexpr const char* value = "time"; };
struct base_dim_current { static constexpr const char* value = "current"; };
struct base_dim_temperature { static constexpr const char* value = "temperature"; };
struct base_dim_substance { static constexpr const char* value = "substance"; };
struct base_dim_luminous_intensity { static constexpr const char* value = "luminous intensity"; };
```
#### `make_dimension` #### `make_dimension`
Above design of dimensions is created with the ease of use for end users in mind. Compile-time Above design of dimensions is created with the ease of use for end users in mind. Compile-time
@@ -169,6 +186,23 @@ contained base dimensions. Beside providing ordering to base dimensions it also
- aggregate two arguments of the same base dimension but different exponents - aggregate two arguments of the same base dimension but different exponents
- eliminate two arguments of the same base dimension and with opposite equal exponents - eliminate two arguments of the same base dimension and with opposite equal exponents
`make_dimension_t` is also able to form a dimension type based not only on base dimensions but
it can take other derived dimensions as well. So for some more complex dimensions user can
type either:
```cpp
struct pressure : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, -1>, exp<base_dim_time, -2>> {};
```
or
```cpp
struct pressure : make_dimension_t<exp<force, 1>, exp<area, -1>> {};
```
In the second case `make_dimension_t` will extract all derived dimensions into the list of
exponents of base dimensions. Thanks to that both cases will result with exactly the same base
class formed only from the exponents of base units.
#### `merge_dimension` #### `merge_dimension`
@@ -426,7 +460,7 @@ struct downcast_base {
}; };
template<typename T> template<typename T>
concept bool Downcastable = concept Downcastable =
requires { requires {
typename T::base_type; typename T::base_type;
} && } &&

View File

@@ -29,81 +29,49 @@
namespace std::experimental::units { namespace std::experimental::units {
struct base_dimension { template<typename T>
const char* name; concept bool BaseDimension = std::is_empty_v<T> &&
requires {
{ T::value } -> std::same_as<const char*>;
}; };
constexpr bool operator==(const base_dimension& lhs, const base_dimension& rhs) namespace detail {
{
const char* p1 = lhs.name;
const char* p2 = rhs.name;
for(; (*p1 != '\0') && (*p2 != '\0'); ++p1, (void)++p2) {
if(*p1 != *p2) return false;
}
return *p1 == *p2;
}
constexpr bool operator<(const base_dimension& lhs, const base_dimension& rhs) template<BaseDimension D1, BaseDimension D2>
constexpr bool less()
{ {
const char* p1 = lhs.name; const char* p1 = D1::value;
const char* p2 = rhs.name; const char* p2 = D2::value;
for(; (*p1 != '\0') && (*p2 != '\0'); ++p1, (void)++p2) { for(; (*p1 != '\0') && (*p2 != '\0'); ++p1, (void) ++p2) {
if(*p1 < *p2) return true; if(*p1 < *p2) return true;
if(*p2 < *p1) return false; if(*p2 < *p1) return false;
} }
return (*p1 == '\0') && (*p2 != '\0'); return (*p1 == '\0') && (*p2 != '\0');
} }
}
// base_dimension_less // base_dimension_less
template<const base_dimension& D1, const base_dimension& D2> template<BaseDimension D1, BaseDimension D2>
struct base_dimension_less : std::bool_constant<D1 < D2> { struct base_dimension_less : std::bool_constant<detail::less<D1, D2>()> {
};
// exp
template<const base_dimension& BaseDimension, int Num, int Den = 1>
struct exp {
static constexpr const base_dimension& dimension = BaseDimension;
static constexpr int num = Num;
static constexpr int den = Den;
}; };
// is_exp // is_exp
namespace detail { namespace detail {
template<typename T> template<typename T>
inline constexpr bool is_exp = false; inline constexpr bool is_exp = false;
template<const base_dimension& BaseDimension, int Num, int Den> // partial specialization for an exp type provided below
inline constexpr bool is_exp<exp<BaseDimension, Num, Den>> = true;
} // namespace detail } // namespace detail
template<typename T> template<typename T>
concept bool Exponent = detail::is_exp<T>; concept bool Exponent = detail::is_exp<T>;
// exp_dim_id_less
template<Exponent E1, Exponent E2>
struct exp_less : base_dimension_less<E1::dimension, E2::dimension> {
};
// exp_invert
template<Exponent E>
struct exp_invert;
template<const base_dimension& BaseDimension, int Num, int Den>
struct exp_invert<exp<BaseDimension, Num, Den>> {
using type = exp<BaseDimension, -Num, Den>;
};
template<Exponent E>
using exp_invert_t = exp_invert<E>::type;
// dimension
template<Exponent... Es> template<Exponent... Es>
struct dimension : downcast_base<dimension<Es...>> {}; struct dimension;
// is_dimension // is_dimension
namespace detail { namespace detail {
@@ -121,6 +89,57 @@ namespace std::experimental::units {
std::is_empty_v<T> && std::is_empty_v<T> &&
detail::is_dimension<downcast_from<T>>; detail::is_dimension<downcast_from<T>>;
// exp
template<typename Dim, int Num, int Den = 1>
requires BaseDimension<Dim> || Dimension<Dim>
struct exp {
using dimension = Dim;
static constexpr int num = Num;
static constexpr int den = Den;
};
// is_exp
namespace detail {
template<typename Dim, int Num, int Den>
inline constexpr bool is_exp<exp<Dim, Num, Den>> = true;
} // namespace detail
// exp_less
template<Exponent E1, Exponent E2>
struct exp_less : base_dimension_less<typename E1::dimension, typename E2::dimension> {
};
// exp_invert
template<Exponent E>
struct exp_invert;
template<typename Dim, int Num, int Den>
struct exp_invert<exp<Dim, Num, Den>> {
using type = exp<Dim, -Num, Den>;
};
template<Exponent E>
using exp_invert_t = exp_invert<E>::type;
// exp_multiply
template<Exponent E, int Num, int Den>
struct exp_multiply {
using type = exp<typename E::dimension, E::num * Num, E::den * Den>;
};
template<Exponent E, int Num, int Den>
using exp_multiply_t = exp_multiply<E, Num, Den>::type;
// dimension
template<Exponent... Es>
struct dimension : downcast_base<dimension<Es...>> {};
// dim_invert // dim_invert
@@ -161,7 +180,7 @@ namespace std::experimental::units {
using type = conditional<std::is_same_v<rest, dimension<>>, dimension<E1>, type_list_push_front<rest, E1>>; using type = conditional<std::is_same_v<rest, dimension<>>, dimension<E1>, type_list_push_front<rest, E1>>;
}; };
template<const base_dimension& D, int Num1, int Den1, int Num2, int Den2, typename... ERest> template<BaseDimension D, int Num1, int Den1, int Num2, int Den2, typename... ERest>
struct dim_consolidate<dimension<exp<D, Num1, Den1>, exp<D, Num2, Den2>, ERest...>> { struct dim_consolidate<dimension<exp<D, Num1, Den1>, exp<D, Num2, Den2>, ERest...>> {
// todo: provide custom implementation for ratio_add // todo: provide custom implementation for ratio_add
using r1 = std::ratio<Num1, Den1>; using r1 = std::ratio<Num1, Den1>;
@@ -171,11 +190,37 @@ namespace std::experimental::units {
dim_consolidate_t<dimension<exp<D, r::num, r::den>, ERest...>>>; dim_consolidate_t<dimension<exp<D, r::num, r::den>, ERest...>>>;
}; };
template<Exponent... Es>
struct extract;
template<Exponent... Es>
using extract_t = extract<Es...>::type;
template<>
struct extract<> {
using type = dimension<>;
};
template<BaseDimension Dim, int Num, int Den, Exponent... ERest>
struct extract<exp<Dim, Num, Den>, ERest...> {
using type = type_list_push_front<extract_t<ERest...>, exp<Dim, Num, Den>>;
};
template<Exponent... Es, int Num, int Den, Exponent... ERest>
struct extract<exp<dimension<Es...>, Num, Den>, ERest...> {
using type = type_list_push_front<extract_t<ERest...>, exp_multiply_t<Es, Num, Den>...>;
};
template<Dimension Dim, int Num, int Den, Exponent... ERest>
struct extract<exp<Dim, Num, Den>, ERest...> {
using type = extract_t<exp<downcast_from<Dim>, Num, Den>, ERest...>;
};
} // namespace detail } // namespace detail
template<Exponent... Es> template<Exponent... Es>
struct make_dimension { struct make_dimension {
using type = detail::dim_consolidate_t<type_list_sort<dimension<Es...>, exp_less>>; using type = detail::dim_consolidate_t<type_list_sort<detail::extract_t<Es...>, exp_less>>;
}; };
template<Exponent... Es> template<Exponent... Es>

View File

@@ -26,7 +26,7 @@
namespace std::experimental::units { namespace std::experimental::units {
struct acceleration : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -2>> {}; struct acceleration : make_dimension_t<exp<velocity, 1>, exp<base_dim_time, -1>> {};
template<> struct downcasting_traits<downcast_from<acceleration>> : downcast_to<acceleration> {}; template<> struct downcasting_traits<downcast_from<acceleration>> : downcast_to<acceleration> {};
template<typename T> template<typename T>

View File

@@ -26,12 +26,12 @@
namespace std::experimental::units { namespace std::experimental::units {
inline constexpr base_dimension base_dim_length{"length"}; struct base_dim_length { static constexpr const char* value = "length"; };
inline constexpr base_dimension base_dim_mass{"mass"}; struct base_dim_mass { static constexpr const char* value = "mass"; };
inline constexpr base_dimension base_dim_time{"time"}; struct base_dim_time { static constexpr const char* value = "time"; };
inline constexpr base_dimension base_dim_current{"current"}; struct base_dim_current { static constexpr const char* value = "current"; };
inline constexpr base_dimension base_dim_temperature{"temperature"}; struct base_dim_temperature { static constexpr const char* value = "temperature"; };
inline constexpr base_dimension base_dim_substance{"substance"}; struct base_dim_substance { static constexpr const char* value = "substance"; };
inline constexpr base_dimension base_dim_luminous_intensity{"luminous intensity"}; struct base_dim_luminous_intensity { static constexpr const char* value = "luminous intensity"; };
} // namespace std::experimental::units } // namespace std::experimental::units

View File

@@ -28,7 +28,7 @@
namespace std::experimental::units { namespace std::experimental::units {
struct capacitance : make_dimension_t<exp<base_dim_mass, -1>, exp<base_dim_length, -2>, exp<base_dim_time, 4>, exp<base_dim_current, 2>> {}; struct capacitance : make_dimension_t<exp<electric_charge, 1>, exp<voltage, -1>> {};
template<> struct downcasting_traits<downcast_from<capacitance>> : downcast_to<capacitance> {}; template<> struct downcasting_traits<downcast_from<capacitance>> : downcast_to<capacitance> {};
template<typename T> template<typename T>

View File

@@ -28,7 +28,7 @@
namespace std::experimental::units { namespace std::experimental::units {
struct energy : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 2>, exp<base_dim_time, -2>> {}; struct energy : make_dimension_t<exp<force, 1>, exp<length, 1>> {};
template<> struct downcasting_traits<downcast_from<energy>> : downcast_to<energy> {}; template<> struct downcasting_traits<downcast_from<energy>> : downcast_to<energy> {};
template<typename T> template<typename T>

View File

@@ -24,12 +24,11 @@
#include <units/dimensions/base_dimensions.h> #include <units/dimensions/base_dimensions.h>
#include <units/dimensions/mass.h> #include <units/dimensions/mass.h>
#include <units/dimensions/length.h> #include <units/dimensions/acceleration.h>
#include <units/dimensions/time.h>
namespace std::experimental::units { namespace std::experimental::units {
struct force : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 1>, exp<base_dim_time, -2>> {}; struct force : make_dimension_t<exp<base_dim_mass, 1>, exp<acceleration, 1>> {};
template<> struct downcasting_traits<downcast_from<force>> : downcast_to<force> {}; template<> struct downcasting_traits<downcast_from<force>> : downcast_to<force> {};
template<typename T> template<typename T>

View File

@@ -27,7 +27,7 @@
namespace std::experimental::units { namespace std::experimental::units {
struct power : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 2>, exp<base_dim_time, -3>> {}; struct power : make_dimension_t<exp<energy, 1>, exp<base_dim_time, -1>> {};
template<> struct downcasting_traits<downcast_from<power>> : downcast_to<power> {}; template<> struct downcasting_traits<downcast_from<power>> : downcast_to<power> {};
template<typename T> template<typename T>

View File

@@ -24,10 +24,11 @@
#include <units/dimensions/base_dimensions.h> #include <units/dimensions/base_dimensions.h>
#include <units/dimensions/force.h> #include <units/dimensions/force.h>
#include <units/dimensions/area.h>
namespace std::experimental::units { namespace std::experimental::units {
struct pressure : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, -1>, exp<base_dim_time, -2>> {}; struct pressure : make_dimension_t<exp<force, 1>, exp<area, -1>> {};
template<> struct downcasting_traits<downcast_from<pressure>> : downcast_to<pressure> {}; template<> struct downcasting_traits<downcast_from<pressure>> : downcast_to<pressure> {};
template<typename T> template<typename T>

View File

@@ -30,7 +30,7 @@
namespace std::experimental::units { namespace std::experimental::units {
struct voltage : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 2>, exp<base_dim_time, -3>, exp<base_dim_current, -1>> {}; struct voltage : make_dimension_t<exp<power, 1>, exp<base_dim_current, -1>> {};
template<> struct downcasting_traits<downcast_from<voltage>> : downcast_to<voltage> {}; template<> struct downcasting_traits<downcast_from<voltage>> : downcast_to<voltage> {};
template<typename T> template<typename T>

View File

@@ -63,19 +63,19 @@ namespace std::experimental::units {
template<typename E, typename... Rest> template<typename E, typename... Rest>
struct get_unit_base_dim<dimension<E, Rest...>> { struct get_unit_base_dim<dimension<E, Rest...>> {
static_assert(sizeof...(Rest) == 0, "Base unit expected"); static_assert(sizeof...(Rest) == 0, "Base unit expected");
static constexpr const base_dimension& dimension = E::dimension; using dimension = E::dimension;
}; };
template<const base_dimension& BaseDimension, typename... Us> template<BaseDimension BD, typename... Us>
struct get_ratio { struct get_ratio {
using ratio = ::std::experimental::units::ratio<1>; using ratio = ::std::experimental::units::ratio<1>;
}; };
template<const base_dimension& BaseDimension, typename U, typename... Rest> template<BaseDimension BD, typename U, typename... Rest>
struct get_ratio<BaseDimension, U, Rest...> { struct get_ratio<BD, U, Rest...> {
static constexpr const base_dimension& unit_base_dim = get_unit_base_dim<typename U::dimension::base_type>::dimension; using unit_base_dim = get_unit_base_dim<typename U::dimension::base_type>::dimension;
using ratio = conditional<&unit_base_dim == &BaseDimension, typename U::ratio, using ratio = conditional<std::is_same_v<unit_base_dim, BD>, typename U::ratio,
typename get_ratio<BaseDimension, Rest...>::ratio>; typename get_ratio<BD, Rest...>::ratio>;
}; };
template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio> template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio>
@@ -105,7 +105,7 @@ namespace std::experimental::units {
template<typename E, typename... Rest, typename... Us> template<typename E, typename... Rest, typename... Us>
struct derived_ratio<dimension<E, Rest...>, Us...> { struct derived_ratio<dimension<E, Rest...>, Us...> {
using rest_ratio = derived_ratio<dimension<Rest...>, Us...>::ratio; using rest_ratio = derived_ratio<dimension<Rest...>, Us...>::ratio;
using e_ratio = get_ratio<E::dimension, Us...>::ratio; using e_ratio = get_ratio<typename E::dimension, Us...>::ratio;
using ratio = ratio_op<rest_ratio, E::num, E::den, e_ratio>::ratio; using ratio = ratio_op<rest_ratio, E::num, E::den, e_ratio>::ratio;
}; };

View File

@@ -29,7 +29,7 @@ namespace {
using namespace std::experimental; using namespace std::experimental;
inline constexpr units::base_dimension base_dim_digital_information{"digital information"}; struct base_dim_digital_information { static constexpr const char* value = "digital information"; };
struct digital_information : units::make_dimension_t<units::exp<base_dim_digital_information, 1>> {}; struct digital_information : units::make_dimension_t<units::exp<base_dim_digital_information, 1>> {};

View File

@@ -27,16 +27,31 @@ using namespace std::experimental::units;
namespace { namespace {
inline constexpr base_dimension d0{"d0"}; struct d0 { static constexpr const char* value = "d0"; };
inline constexpr base_dimension d1{"d1"}; struct d1 { static constexpr const char* value = "d1"; };
inline constexpr base_dimension d2{"d2"}; struct d2 { static constexpr const char* value = "d2"; };
inline constexpr base_dimension d3{"d3"}; struct d3 { static constexpr const char* value = "d3"; };
// exp_invert // exp_invert
static_assert(std::is_same_v<exp_invert_t<exp<d0, 1>>, exp<d0, -1>>); static_assert(std::is_same_v<exp_invert_t<exp<d0, 1>>, exp<d0, -1>>);
static_assert(std::is_same_v<exp_invert_t<exp<d1, -1>>, exp<d1, 1>>); static_assert(std::is_same_v<exp_invert_t<exp<d1, -1>>, exp<d1, 1>>);
// extract
template<typename T>
struct typeinfo;
static_assert(std::is_same_v<detail::extract_t<>, dimension<>>);
static_assert(std::is_same_v<detail::extract_t<exp<d0, 1>>, dimension<exp<d0, 1>>>);
static_assert(std::is_same_v<detail::extract_t<exp<d0, 1>, exp<d1, 2>>, dimension<exp<d0, 1>, exp<d1, 2>>>);
using dim0 = dimension<>;
using dim1 = dimension<exp<d0, 1>>;
using dim2 = dimension<exp<d0, 1>, exp<d1, 2>>;
static_assert(std::is_same_v<detail::extract_t<exp<dim0, 2>, exp<d0, 1>>, dimension<exp<d0, 1>>>);
static_assert(std::is_same_v<detail::extract_t<exp<dim1, 2>, exp<d0, 1>>, dimension<exp<d0, 2>, exp<d0, 1>>>);
static_assert(std::is_same_v<detail::extract_t<exp<dim2, -2>, exp<d0, 1>, exp<d1, 2>>, dimension<exp<d0, -2>, exp<d1, -4>, exp<d0, 1>, exp<d1, 2>>>);
// make_dimension // make_dimension
static_assert(std::is_same_v<make_dimension_t<exp<d0, 1>>, dimension<exp<d0, 1>>>); static_assert(std::is_same_v<make_dimension_t<exp<d0, 1>>, dimension<exp<d0, 1>>>);

View File

@@ -82,8 +82,8 @@ namespace {
std::is_same_v<type_list_split_half<type_list<int, long, double, float>>::second_list, type_list<double, float>>); std::is_same_v<type_list_split_half<type_list<int, long, double, float>>::second_list, type_list<double, float>>);
// type_list_merge_sorted // type_list_merge_sorted
inline constexpr base_dimension d0{"d0"}; struct d0 { static constexpr const char* value = "d0"; };
inline constexpr base_dimension d1{"d1"}; struct d1 { static constexpr const char* value = "d1"; };
static_assert(std::is_same_v<type_list_merge_sorted<type_list<exp<d0, 1>>, type_list<exp<d1, 1>>, exp_less>, static_assert(std::is_same_v<type_list_merge_sorted<type_list<exp<d0, 1>>, type_list<exp<d1, 1>>, exp_less>,
type_list<exp<d0, 1>, exp<d1, 1>>>); type_list<exp<d0, 1>, exp<d1, 1>>>);