From d31d2cf439cfbd2efc5e0e2373e6997135e99762 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 15 Oct 2019 19:03:45 +0200 Subject: [PATCH] Base dimensions refactored - --- README.md | 2 + doc/DESIGN.md | 38 ++++++---- src/include/units/bits/fixed_string.h | 75 +++++++++++++------ src/include/units/dimension.h | 31 +++----- .../units/dimensions/base_dimensions.h | 14 ++-- src/include/units/unit.h | 1 - test/unit_test/static/custom_unit_test.cpp | 3 +- test/unit_test/static/dimension_test.cpp | 8 +- test/unit_test/static/type_list_test.cpp | 4 +- 9 files changed, 108 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 19272a1b..5ff436fe 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ NOTE: This library as of now compiles correctly only with gcc-9.1 and newer. - Added `pow()` and `sqrt()` operations on quantities - `units` removed from a `std::experimental` namespace - Downcasting facility refactored so the user does not have to write the boilerplate code anymore + - From now on base dimensions should inherit from `base_dimension` class template + - Added unit symbols definitions to `base_dimension` and `derived_unit` - 0.3.1 Sep 18, 2019 - cmcstl2 dependency changed to range-v3 0.9.1 diff --git a/doc/DESIGN.md b/doc/DESIGN.md index ae57e620..f43c56ec 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -119,27 +119,39 @@ struct exp { 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: +`units::base_dimension` represents a base dimension and has assigned a unique compile-time text +describing the dimension name: + +```cpp +template +struct base_dimension { + using name = Name; + using symbol = Symbol; +}; +``` + +`units::BaseDimension` is a concept to match all types derived from `base_dimension` instantiations: ```cpp template concept BaseDimension = std::is_empty_v && - requires { - { T::value } -> std::same_as; - }; + requires { + typename T::name; + typename T::symbol; + } && + std::derived_from>; ``` 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"; }; +struct base_dim_length : base_dimension<"length", "m"> {}; +struct base_dim_mass : base_dimension<"mass", "kg"> {}; +struct base_dim_time : base_dimension<"time", "s"> {}; +struct base_dim_current : base_dimension<"current", "A"> {}; +struct base_dim_temperature : base_dimension<"temperature", "K"> {}; +struct base_dim_substance : base_dimension<"substance", "mol"> {}; +struct base_dim_luminous_intensity : base_dimension<"luminous intensity", "cd"> {}; ``` #### `derived_dimension` @@ -624,7 +636,7 @@ In order to extend the library with custom dimensions the user has to: 1. Create a new base dimension if the predefined ones are not enough to form a new derived dimension: ```cpp - inline constexpr units::base_dimension base_dim_digital_information{"digital information"}; + struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; ``` 2. Create a new dimension type with the recipe of how to construct it from base dimensions and diff --git a/src/include/units/bits/fixed_string.h b/src/include/units/bits/fixed_string.h index 47df025d..8d6654ce 100644 --- a/src/include/units/bits/fixed_string.h +++ b/src/include/units/bits/fixed_string.h @@ -24,31 +24,64 @@ namespace units { -// TODO gcc:92101 -// Gated by the following gcc bug -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92101 -// -// template -// struct basic_fixed_string { -// CharT data_[N+1] = {}; + template + struct basic_fixed_string { + CharT data_[N+1] = {}; -// constexpr basic_fixed_string(const CharT (&txt)[N+1]) noexcept -// { -// for(std::size_t i = 0; i <= N; ++i) -// data_[i] = txt[i]; -// } -// // auto operator==(const basic_fixed_string &) = default; -// [[nodiscard]] constexpr const CharT* c_str() const noexcept { return data_; } -// }; + constexpr basic_fixed_string(const CharT (&txt)[N+1]) noexcept + { + for(std::size_t i = 0; i <= N; ++i) + data_[i] = txt[i]; + } -// template -// basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string; + [[nodiscard]] constexpr bool size() const noexcept { return N; } + [[nodiscard]] constexpr const CharT* c_str() const noexcept { return data_; } -// template -// using fixed_string = basic_fixed_string; + // auto operator==(const basic_fixed_string &) = default; + + [[nodiscard]] constexpr friend bool operator==(const basic_fixed_string& lhs, const basic_fixed_string& rhs) noexcept + { + for(size_t i = 0; i != size(lhs.data_); ++i) + if(lhs.name_[i] != rhs.data_[i]) + return false; + return true; + } + + template + [[nodiscard]] constexpr friend bool operator==(const basic_fixed_string&, const basic_fixed_string&) noexcept + { + return false; + } + + template + [[nodiscard]] constexpr friend bool operator<(const basic_fixed_string& lhs, const basic_fixed_string& rhs) noexcept + { + using std::begin, std::end; + auto first1 = begin(lhs.data_); + auto first2 = begin(rhs.data_); + const auto last1 = std::prev(end(lhs.data_)); // do not waste time for '\0' + const auto last2 = std::prev(end(rhs.data_)); + + for(; (first1 != last1) && (first2 != last2); ++first1, (void)++first2 ) { + if(*first1 < *first2) return true; + if(*first2 < *first1) return false; + } + return first1 == last1 && first2 != last2; + } + }; + + template + basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string; + + template + using fixed_string = basic_fixed_string; + + // TODO gcc:92101 + // hacked version to work with derived_unit + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92101 template - struct basic_fixed_string { + struct basic_fixed_string_hack { static constexpr CharT txt[] = { Chars..., '\0' }; static constexpr const CharT* c_str() noexcept @@ -60,7 +93,7 @@ namespace units { inline namespace hacks { template - constexpr basic_fixed_string operator""_fs() { return {}; } + constexpr basic_fixed_string_hack operator""_fs() { return {}; } } diff --git a/src/include/units/dimension.h b/src/include/units/dimension.h index 3421028b..280d5145 100644 --- a/src/include/units/dimension.h +++ b/src/include/units/dimension.h @@ -24,37 +24,30 @@ #include #include +#include #include #include namespace units { + template + struct base_dimension { + static constexpr auto name = Name; + static constexpr auto symbol = Symbol; + }; + template concept BaseDimension = std::is_empty_v && requires { - { T::value } -> std::same_as; - }; - - namespace detail { - - template - constexpr bool less() - { - const char* p1 = D1::value; - const char* p2 = D2::value; - for(; (*p1 != '\0') && (*p2 != '\0'); ++p1, (void) ++p2) { - if(*p1 < *p2) return true; - if(*p2 < *p1) return false; - } - return (*p1 == '\0') && (*p2 != '\0'); - } - - } + T::name; + T::symbol; + };// && // TODO file a bug for this gcc issue +// std::derived_from>; // base_dimension_less template - struct base_dimension_less : std::bool_constant()> { + struct base_dimension_less : std::bool_constant { }; // is_exp diff --git a/src/include/units/dimensions/base_dimensions.h b/src/include/units/dimensions/base_dimensions.h index 81314a0b..ef2ea824 100644 --- a/src/include/units/dimensions/base_dimensions.h +++ b/src/include/units/dimensions/base_dimensions.h @@ -26,12 +26,12 @@ namespace units { - 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"; }; + struct base_dim_length : base_dimension<"length", "m"> {}; + struct base_dim_mass : base_dimension<"mass", "kg"> {}; + struct base_dim_time : base_dimension<"time", "s"> {}; + struct base_dim_current : base_dimension<"current", "A"> {}; + struct base_dim_temperature : base_dimension<"temperature", "K"> {}; + struct base_dim_substance : base_dimension<"substance", "mol"> {}; + struct base_dim_luminous_intensity : base_dimension<"luminous intensity", "cd"> {}; } // namespace units diff --git a/src/include/units/unit.h b/src/include/units/unit.h index bf5a58ed..4fe06378 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -24,7 +24,6 @@ #include #include -#include #include namespace units { diff --git a/test/unit_test/static/custom_unit_test.cpp b/test/unit_test/static/custom_unit_test.cpp index e2cf5bcd..f0a28850 100644 --- a/test/unit_test/static/custom_unit_test.cpp +++ b/test/unit_test/static/custom_unit_test.cpp @@ -28,7 +28,7 @@ namespace { - struct base_dim_digital_information { static constexpr const char* value = "digital information"; }; + struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; struct digital_information : units::derived_dimension> {}; @@ -36,6 +36,7 @@ namespace { concept DigitalInformation = units::QuantityOf; using namespace units::hacks; + struct bit : units::derived_unit {}; struct byte : units::derived_unit> {}; diff --git a/test/unit_test/static/dimension_test.cpp b/test/unit_test/static/dimension_test.cpp index 78d92dba..368a2c38 100644 --- a/test/unit_test/static/dimension_test.cpp +++ b/test/unit_test/static/dimension_test.cpp @@ -27,10 +27,10 @@ using namespace units; namespace { - struct d0 { static constexpr const char* value = "d0"; }; - struct d1 { static constexpr const char* value = "d1"; }; - struct d2 { static constexpr const char* value = "d2"; }; - struct d3 { static constexpr const char* value = "d3"; }; + struct d0 : base_dimension<"d0", ""> {}; + struct d1 : base_dimension<"d1", ""> {}; + struct d2 : base_dimension<"d2", ""> {}; + struct d3 : base_dimension<"d3", ""> {}; // exp_invert diff --git a/test/unit_test/static/type_list_test.cpp b/test/unit_test/static/type_list_test.cpp index 491ede6a..12d89c50 100644 --- a/test/unit_test/static/type_list_test.cpp +++ b/test/unit_test/static/type_list_test.cpp @@ -83,8 +83,8 @@ namespace { std::is_same_v>::second_list, type_list>); // type_list_merge_sorted - struct d0 { static constexpr const char* value = "d0"; }; - struct d1 { static constexpr const char* value = "d1"; }; + struct d0 : base_dimension<"d0", ""> {}; + struct d1 : base_dimension<"d1", ""> {}; static_assert(std::is_same_v>, type_list>, exp_less>, type_list, exp>>);