diff --git a/include/intersection/detail.hh b/include/intersection/detail.hh index e273cc0..92e019d 100644 --- a/include/intersection/detail.hh +++ b/include/intersection/detail.hh @@ -7,6 +7,7 @@ #include "distance/distance.hh" #include "primitives/primitives.hh" #include "primitives/vec2.hh" +#include "utils/utils.hh" namespace geom::detail { @@ -128,10 +129,9 @@ Segment2D helperMollerHaines(const Triangle &tr, const Plane &pl, const std::size_t rogue = roguePos(sdist.begin(), sdist.end()); std::array segm{}; - std::array arr{(rogue + 1) % 3, (rogue + 2) % 3}; - std::transform(arr.begin(), arr.end(), segm.begin(), [&vert, &sdist, rogue](auto i) { - return vert[i] + (vert[rogue] - vert[i]) * sdist[i] / (sdist[i] - sdist[rogue]); - }); + std::array arr = {(rogue + 1) % 3, (rogue + 2) % 3}; + for (auto [idx, i] : utils::Enumerate(arr)) + segm[idx] = vert[i] + (vert[rogue] - vert[i]) * sdist[i] / (sdist[i] - sdist[rogue]); return std::minmax(segm[0], segm[1]); } diff --git a/include/utils/utils.hh b/include/utils/utils.hh new file mode 100644 index 0000000..9d51090 --- /dev/null +++ b/include/utils/utils.hh @@ -0,0 +1,137 @@ +#ifndef __INCLUDE_UTILS_UTILS_HH__ +#define __INCLUDE_UTILS_UTILS_HH__ + +#include +#include +#include + +namespace utils +{ +template +concept ItContainer = requires(T cont) +{ + { + std::begin(cont) + } -> std::forward_iterator; + { + std::end(cont) + } -> std::forward_iterator; + { + std::size(cont) + } -> std::convertible_to; +}; + +namespace detail +{ +template +class EnumerateIt final +{ +private: + template + struct ArrowProxy + { + Ref r; + + Ref *operator->() + { + return &r; + } + }; + +public: + using iterator_category = std::forward_iterator_tag; + using difference_type = + std::pair, typename std::iterator_traits::difference_type>; + using value_type = std::pair::value_type>; + using reference = std::pair::reference>; + using pointer = ArrowProxy; + +private: + std::size_t counter_; + It iter_; + +public: + EnumerateIt(std::size_t i, It iter) : counter_(i), iter_(iter) + {} + + reference operator*() const + { + return {counter_, *iter_}; + } + + EnumerateIt &operator++() + { + ++counter_; + ++iter_; + return *this; + } + + EnumerateIt operator++(int) + { + auto temp{*this}; + operator++(); + return temp; + } + + bool equals(const EnumerateIt &rhs) const + { + return iter_ == rhs.iter_; + } + + pointer operator->() const + { + return pointer{{counter_, *iter_}}; + } +}; + +template +bool operator==(const EnumerateIt &lhs, const EnumerateIt &rhs) +{ + return lhs.equals(rhs); +} +} // namespace detail + +template +class Enumerate final +{ +private: + using NoRefC = std::remove_reference_t; + using ContStorageType = std::conditional_t, NoRefC, NoRefC &>; + + ContStorageType cont_; + using EnumItType = detail::EnumerateIt; + +public: + Enumerate(C &&cont) : cont_(std::forward(cont)) + {} + + auto begin() + { + return EnumItType(0, std::begin(cont_)); + } + auto begin() const + { + return EnumItType(0, std::begin(cont_)); + } + + auto end() + { + return EnumItType(std::size(cont_), std::end(cont_)); + } + auto end() const + { + return EnumItType(std::size(cont_), std::end(cont_)); + } + + auto size() const + { + return std::size(cont_); + } +}; + +template +Enumerate(C &&) -> Enumerate; + +} // namespace utils + +#endif /* __INCLUDE_UTILS_UTILS_HH__ */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6462b00..e227e29 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LIBLIST primitives intersection distance kdtree) +set(LIBLIST primitives intersection distance kdtree utils) foreach(LIB ${LIBLIST}) add_subdirectory(${LIB}) diff --git a/lib/utils/CMakeLists.txt b/lib/utils/CMakeLists.txt new file mode 100644 index 0000000..6f47533 --- /dev/null +++ b/lib/utils/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(utils INTERFACE) +target_sources(utils + INTERFACE ${CMAKE_SOURCE_DIR}/include/utils/utils.hh +) diff --git a/test/unit/utils.cc b/test/unit/utils.cc new file mode 100644 index 0000000..7ea8bde --- /dev/null +++ b/test/unit/utils.cc @@ -0,0 +1,188 @@ +#include "utils/utils.hh" + +#include "test_header.hh" +#include +#include +#include + +using namespace utils; + +TEST(EnumerateTest, ordVecSimpleBind) +{ + // Arrange + std::vector vec; + vec.resize(10); + std::iota(vec.begin(), vec.end(), 0); + + // Act && Asserts + for (auto val : Enumerate(vec)) + ASSERT_EQ(val.first, val.second); +} + +TEST(EnumerateTest, ordVecStructBind) +{ + // Arrange + std::vector vec; + vec.resize(10); + std::iota(vec.begin(), vec.end(), 0); + + // Act && Asserts + for (auto [i, val] : Enumerate(vec)) + ASSERT_EQ(i, val); +} + +TEST(EnumerateTest, ordVecChangeValue) +{ + // Arrange + std::vector vec, expect; + vec.resize(10); + expect.resize(vec.size()); + std::iota(vec.begin(), vec.end(), 0); + std::iota(expect.rbegin(), expect.rend(), -expect.size() + 1); + + // Act + for (auto [i, val] : Enumerate(vec)) + val = -static_cast(i); + + // Assert + EXPECT_EQ(vec, expect); +} + +TEST(EnumerateTest, twiceDeref) +{ + // Arrange + std::vector vec = {1, 2, 3}; + + // Act + auto enumerate = Enumerate(vec); + auto it1 = enumerate.begin(); + auto it2 = enumerate.begin(); + + using value_type = + std::iterator_traits::iterator>>::value_type; + + value_type v1 = *it1++; + value_type v2 = *it1; + + auto r1 = *it2; + ++it2; + auto r2 = *it2; + + // Assert + EXPECT_EQ(v1.first, 0); + EXPECT_EQ(v2.first, 1); + + EXPECT_NE(r1, r2); +} + +TEST(EnumerateTest, ordVecTemp) +{ + // Arrange + std::vector res, expect(10, 10); + res.resize(10); + + // Act + for (auto [i, val] : Enumerate(std::vector(expect.size(), 10))) + res[i] = val; + + // Assert + EXPECT_EQ(res, expect); +} + +TEST(EnumerateTest, arrowProxy) +{ + // Arrange + struct Compl + { + int a; + + Compl &operator=(int v) + { + a = v; + return *this; + } + + bool operator==(const Compl &) const = default; + + void inc() + { + a++; + } + }; + + std::vector vec, expect; + vec.resize(10); + expect.resize(vec.size()); + std::iota(vec.begin(), vec.end(), 0); + std::iota(expect.begin(), expect.end(), 1); + + // Act + auto enumerate = Enumerate(vec); + for (auto it = enumerate.begin(); it != enumerate.end(); it++) + it->second.inc(); + + // Assert + EXPECT_EQ(vec, expect); +} + +TEST(EnumerateTest, constContainer) +{ + // Arrange + const std::vector vec = {1, 2, 3, 4, 5}; + std::vector vec2(vec.size()); + + // Act + for (auto [idx, val] : Enumerate(vec)) + vec2[idx] = val; + + // Assert + EXPECT_EQ(vec, vec2); +} + +TEST(EnumerateTest, MoveSemantics) +{ + // Arrange + struct WasMovedType + {}; + struct WasCopiedType + {}; + struct ToMove + { + private: + std::vector dummy{}; + + public: + ToMove() = default; + + ToMove(const ToMove &that) : dummy(that.dummy) + { + throw WasCopiedType{}; + } + ToMove(ToMove &&that) : dummy(std::move(that.dummy)) + { + throw WasMovedType{}; + } + + auto size() const + { + return dummy.size(); + } + + auto begin() + { + return dummy.begin(); + } + + auto end() + { + return dummy.end(); + } + }; + + ToMove local; + // Act && Assert + EXPECT_THROW([[maybe_unused]] auto moved = Enumerate(ToMove{});, WasMovedType); + EXPECT_NO_THROW([[maybe_unused]] auto copied = Enumerate(local);); +} + +#include "test_footer.hh"