-
-
Notifications
You must be signed in to change notification settings - Fork 116
Implement simple vector and tensor types #735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
20a6db0 to
7bc1e18
Compare
mpusz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi!
Thanks for contributing! I had time to review only cartesian_vector changes and provided some feedback that I believe will also be meaningful to another type as well.
| #include <mp-units/bits/requires_hosted.h> | ||
| // | ||
| #include <mp-units/bits/module_macros.h> | ||
| #include <mp-units/bits/requires_hosted.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We put requires_hosted.h as the first header in all HOSTED implementations. Do not use alphabetical order for this header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still left to resolve.
| namespace mp_units { | ||
|
|
||
| MP_UNITS_EXPORT template<detail::Scalar T> | ||
| MP_UNITS_EXPORT template<detail::Scalar T = double> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to provide template defaults in the implementation rather than in one of (possibly many) forward declarations.
| requires requires(T t, U u) { t + u; } | ||
| requires requires(const T& t, const U& u) { t + u; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make any difference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've read that adding const doesn't mutate an input which is desired in arithmetic operations (https://en.cppreference.com/w/cpp/language/operator_arithmetic.html)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right: https://godbolt.org/z/3zW8ns6YP. Thanks!
| namespace detail { | ||
|
|
||
| struct cartesian_vector_iface { | ||
| // A + B |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Such comments do not improve anything.
| // A % B (integral: %, floating: fmod into CT) | ||
| template<typename T, typename U> | ||
| requires requires(T t, U u) { t * u; } | ||
| [[nodiscard]] friend constexpr auto operator*(const cartesian_vector<T>& lhs, const U& rhs) | ||
| requires(requires(const T& t, const U& u) { t % u; }) || (std::floating_point<T> && std::floating_point<U>) | ||
| [[nodiscard]] friend constexpr auto operator%(const cartesian_vector<T>& lhs, const cartesian_vector<U>& rhs) | ||
| { | ||
| using CT = std::common_type_t<T, U>; | ||
| if constexpr (std::floating_point<T> && std::floating_point<U>) { | ||
| using std::fmod; | ||
| return ::mp_units::cartesian_vector<CT>{static_cast<CT>(fmod(static_cast<long double>(lhs._coordinates_[0]), | ||
| static_cast<long double>(rhs._coordinates_[0]))), | ||
| static_cast<CT>(fmod(static_cast<long double>(lhs._coordinates_[1]), | ||
| static_cast<long double>(rhs._coordinates_[1]))), | ||
| static_cast<CT>(fmod(static_cast<long double>(lhs._coordinates_[2]), | ||
| static_cast<long double>(rhs._coordinates_[2])))}; | ||
| } else { | ||
| return ::mp_units::cartesian_vector<CT>{static_cast<CT>(lhs._coordinates_[0] % rhs._coordinates_[0]), | ||
| static_cast<CT>(lhs._coordinates_[1] % rhs._coordinates_[1]), | ||
| static_cast<CT>(lhs._coordinates_[2] % rhs._coordinates_[2])}; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no op% for floating-point types for a reason. We should behave like fundamental types, so it should not be available as well for floating-point representation types. If we care about modulo for such types, we should provide fmod non-member function. See fmod from math.h for more info.
| template<typename FormatContext> | ||
| auto format(const mp_units::cartesian_vector<T>& vec, FormatContext& ctx) const | ||
| template<typename Ctx> | ||
| auto format(const mp_units::cartesian_vector<T>& v, Ctx& ctx) const | ||
| { | ||
| return format_to(ctx.out(), "[{}, {}, {}]", vec[0], vec[1], vec[2]); | ||
| return format_to(ctx.out(), "[{}, {}, {}]", v[0], v[1], v[2]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please restore the previous code. FormatContext provides more info and is used consistently in the entire project for all formatters.
v is too short as an identifier and often causes some stupid shadowing errors in MSVC.
| import mp_units; | ||
| #else | ||
| #include <mp-units/cartesian_vector.h> | ||
| #if MP_UNITS_HOSTED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Runtime tests are only run for HOSTED so no need to check here.
| REQUIRE(v[0] == 0); | ||
| REQUIRE(v[1] == 0); | ||
| REQUIRE(v[2] == 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be garbage
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is why I didn't provide default construction.
|
|
||
| SECTION("zero arguments") | ||
| { | ||
| cartesian_vector<double> v{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how zeros should be set, but this collides with your default-constructor now
| using namespace Catch::Matchers; | ||
| using Catch::Matchers::WithinAbs; | ||
|
|
||
| TEST_CASE("cartesian_vector initialization and access", "[vector]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer to have fewer TEST_CASES in catch, so it is easier to manage. For example, if I want to check if all vector tests still work, it can check the status of one easy-to-find test rather than searching for many different strings.
Filtering by a tag is not often an option.
|
Hi! Thank you so much! I'll start reading and implementing changes - thank you and sorry in advance for any problems! :) |
| cartesian_vector() = default; | ||
| cartesian_vector(const cartesian_vector&) = default; | ||
| cartesian_vector(cartesian_vector&&) = default; | ||
| cartesian_vector& operator=(const cartesian_vector&) = default; | ||
| cartesian_vector& operator=(cartesian_vector&&) = default; | ||
|
|
||
| template<typename... Args> | ||
| requires(... && std::constructible_from<T, Args>) | ||
| requires(sizeof...(Args) <= 3) && (... && std::constructible_from<T, Args>) | ||
| constexpr explicit(!(... && std::convertible_to<Args, T>)) cartesian_vector(Args&&... args) : | ||
| _coordinates_{static_cast<T>(std::forward<Args>(args))...} | ||
| { | ||
| } | ||
|
|
||
| template<typename U> | ||
| requires std::constructible_from<T, U> | ||
| requires std::constructible_from<T, const U&> | ||
| constexpr explicit(!std::convertible_to<U, T>) cartesian_vector(const cartesian_vector<U>& other) : | ||
| _coordinates_{static_cast<T>(other[0]), static_cast<T>(other[1]), static_cast<T>(other[2])} | ||
| { | ||
| } | ||
|
|
||
| template<typename U> | ||
| requires std::constructible_from<T, U> | ||
| requires std::constructible_from<T, U&&> | ||
| constexpr explicit(!std::convertible_to<U, T>) cartesian_vector(cartesian_vector<U>&& other) : | ||
| _coordinates_{static_cast<T>(std::move(other[0])), static_cast<T>(std::move(other[1])), | ||
| static_cast<T>(std::move(other[2]))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove all of those. This will result in an aggregate type (such as std::array), which should solve all our issues. Please, change the comment next to a public member to reflect that.
f3f2391 to
00b2e74
Compare
|
@mpusz could you help me fix the pipeline? I tried in some many ways and I have no idea how to fix it :c |
|
I am currently on vacation in Hawaii, so I don't have much time to look into it. I will check it out when I am back. |
1618ca3 to
e564751
Compare
e564751 to
59f9298
Compare
3e231f3 to
1ecb38f
Compare
| #include <mp-units/bits/requires_hosted.h> | ||
| // | ||
| #include <mp-units/bits/module_macros.h> | ||
| #include <mp-units/bits/requires_hosted.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still left to resolve.
| #pragma once | ||
|
|
||
| #include <mp-units/bits/module_macros.h> | ||
| #include <mp-units/bits/requires_hosted.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be the first include
| MP_UNITS_EXPORT template<typename T, typename U> | ||
| requires requires(const T& t, const U& u) { t + u; } | ||
| [[nodiscard]] constexpr auto operator+(const cartesian_vector<T>& lhs, const cartesian_vector<U>& rhs) | ||
| { | ||
| return ::mp_units::cartesian_vector{lhs._coordinates_[0] + rhs._coordinates_[0], | ||
| lhs._coordinates_[1] + rhs._coordinates_[1], | ||
| lhs._coordinates_[2] + rhs._coordinates_[2]}; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the operators and other customization points should be hidden friends as they were before.
| { | ||
| cartesian_vector<double> v2 = v1; | ||
| cartesian_vector v1{1.0, 2.0, 3.0}; | ||
| cartesian_vector<double> v2 = v1; // Use initialization instead of assignment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an assignment
| static_assert(std::constructible_from<cartesian_vector<double>, quantity<one, cartesian_vector<double>>>); | ||
| static_assert(!std::convertible_to<quantity<one, double>, cartesian_vector<double>>); | ||
| static_assert(std::constructible_from<cartesian_vector<double>, quantity<one, double>>); | ||
| static_assert(std::is_aggregate_v<cartesian_vector<double>>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change makes no sense and removes proper test
| #else | ||
| #include <mp-units/cartesian_vector.h> | ||
| #include <mp-units/ext/format.h> | ||
| #include <sstream> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Included twice
| #endif | ||
|
|
||
| #ifndef MP_UNITS_MODULES | ||
| #include <sstream> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import std; missing.
| REQUIRE_THAT(u.magnitude(), WithinAbs(1.0, 1e-12)); | ||
| REQUIRE_THAT(u[0], WithinAbs(3.0 / 5.0, 1e-12)); | ||
| REQUIRE_THAT(u[1], WithinAbs(4.0 / 5.0, 1e-12)); | ||
| REQUIRE_THAT(u[2], WithinAbs(0.0, 1e-12)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use AlmostEquals instead
| } | ||
| } | ||
|
|
||
| TEST_CASE("cartesian_vector text output", "[vector][fmt][ostream]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove my tests that were testing each text output separately for different representation types?
Added
Types
Vector ops (a,b : cartesian_vector ; s : scalar)
Tensor ops (A,B : cartesian_tensor<R,C> ; M:N, matching dims; s: scalar)