Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,23 @@ All of the benchmarks measure the performance in release mode and run similar sc

All of the measurements were taken on MacBook M3 Pro 18GB.

#### Apply filter and map on 10 000 000 ints
#### Filter and map

Apply filter and map on 1'000'000 integers

| **Benchmark** | **Time [ns]** | **CPU [ns]** |
|:------------------------------:|:-------------:|:------------:|
| benchmarkRustyIterFilterAndMap | 559108 | 559065 |
| benchmarkRustyIterFilterMap | 556122 | 556120 |
| benchmarkRangesFilterTransform | 539868 | 539786 |

Apply filter and map on 10'000'000 integers

| **Benchmark** | **Time [ns]** | **CPU [ns]** |
|:------------------------------:|:-------------:|:------------:|
| benchmarkRustyIterFilterMap | 5565001 | 5564975 |
| benchmarkRangesFilterTransform | 5837398 | 5837397 |
| benchmarkRustyIterFilterAndMap | 5644164 | 5643854 |
| benchmarkRustyIterFilterMap | 5599177 | 5599185 |
| benchmarkRangesFilterTransform | 5988982 | 5988701 |

## Authors

Expand Down
18 changes: 17 additions & 1 deletion benchmarks/iterator.benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ auto initializeIncrementalVector() -> std::vector<int>
return std::move(data);
}

auto benchmarkRustyIterFilterMap(benchmark::State& state) -> void
auto benchmarkRustyIterFilterAndMap(benchmark::State& state) -> void
{
auto data = initializeIncrementalVector();

Expand All @@ -30,6 +30,21 @@ auto benchmarkRustyIterFilterMap(benchmark::State& state) -> void
}
}

auto benchmarkRustyIterFilterMap(benchmark::State& state) -> void
{
auto data = initializeIncrementalVector();

for (auto _ : state)
{
auto f = [](int x) -> std::optional<int> {
if (x % 2 == 0)
return std::make_optional(x * 2);
return std::nullopt;
};
auto result = LazyIterator{data}.filterMap(std::move(f)).collect();
}
}

auto benchmarkRangesFilterTransform(benchmark::State& state) -> void
{
auto data = initializeIncrementalVector();
Expand Down Expand Up @@ -64,6 +79,7 @@ auto benchmarkRustyIterCacheCycle(benchmark::State& state) -> void
}
}

BENCHMARK(benchmarkRustyIterFilterAndMap);
BENCHMARK(benchmarkRustyIterFilterMap);
BENCHMARK(benchmarkRangesFilterTransform);
BENCHMARK(benchmarkRustyIterCopyCycle);
Expand Down
6 changes: 6 additions & 0 deletions include/rusty_iterators/concepts.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <optional>
#include <tuple>

namespace rusty_iterators::concepts
Expand All @@ -26,6 +27,11 @@ concept EqFunctor = requires(Functor f, std::tuple<T, T> t) {
template <class T, class Functor>
concept FilterFunctor = AllFunctor<T, Functor>;

template <class Tin, class Tout, class Functor>
concept FilterMapFunctor = requires(Functor f, Tin t) {
{ f(t) } -> std::same_as<std::optional<Tout>>;
};

template <class B, class T, class Functor>
concept FoldFunctor = requires(Functor f, B first, T second) {
{ f(first, second) } -> std::same_as<B>;
Expand Down
56 changes: 56 additions & 0 deletions include/rusty_iterators/filter_map.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include "concepts.hpp"
#include "interface.fwd.hpp"

#include <optional>

namespace rusty_iterators::iterator
{
using concepts::FilterMapFunctor;
using interface::IterInterface;

template <class Tin, class Tout, class Functor, class Other>
requires FilterMapFunctor<Tin, Tout, Functor>
class FilterMap : public IterInterface<Tout, FilterMap<Tin, Tout, Functor, Other>>
{
public:
explicit FilterMap(Other&& it, Functor&& f)
: it(std::forward<Other>(it)), func(std::forward<Functor>(f))
{}

auto next() -> std::optional<Tout>;
[[nodiscard]] auto sizeHint() const -> std::optional<size_t>;

private:
Other it;
Functor func;
};
} // namespace rusty_iterators::iterator

template <class Tin, class Tout, class Functor, class Other>
requires rusty_iterators::concepts::FilterMapFunctor<Tin, Tout, Functor>
auto rusty_iterators::iterator::FilterMap<Tin, Tout, Functor, Other>::next() -> std::optional<Tout>
{
auto nextItem = it.next();

while (nextItem.has_value())
{
auto result = func(nextItem.value());

if (result.has_value())
{
return std::make_optional(std::move(result.value()));
}
nextItem = it.next();
}
return std::nullopt;
}

template <class Tin, class Tout, class Functor, class Other>
requires rusty_iterators::concepts::FilterMapFunctor<Tin, Tout, Functor>
auto rusty_iterators::iterator::FilterMap<Tin, Tout, Functor, Other>::sizeHint() const
-> std::optional<size_t>
{
return it.sizeHint();
}
20 changes: 20 additions & 0 deletions include/rusty_iterators/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
#include "concepts.hpp"
#include "cycle.hpp"
#include "filter.hpp"
#include "filter_map.hpp"
#include "inspect.hpp"
#include "map.hpp"
#include "moving_window.hpp"
#include "take.hpp"
#include "zip.hpp"

#include <stdexcept>
#include <type_traits>
#include <vector>

namespace rusty_iterators::interface
Expand All @@ -20,6 +22,7 @@ using concepts::AnyFunctor;
using concepts::Comparable;
using concepts::EqFunctor;
using concepts::FilterFunctor;
using concepts::FilterMapFunctor;
using concepts::FoldFunctor;
using concepts::ForEachFunctor;
using concepts::Indexable;
Expand All @@ -33,6 +36,7 @@ using iterator::Chain;
using iterator::CopyCycle;
using iterator::CycleType;
using iterator::Filter;
using iterator::FilterMap;
using iterator::Inspect;
using iterator::Map;
using iterator::MovingWindow;
Expand Down Expand Up @@ -88,6 +92,11 @@ class IterInterface
requires FilterFunctor<T, Functor>
[[nodiscard]] auto filter(Functor&& f) -> Filter<T, Functor, Derived>;

template <class Functor>
requires FilterMapFunctor<T, typename std::invoke_result_t<Functor, T>::value_type, Functor>
[[nodiscard]] auto filterMap(Functor&& f)
-> FilterMap<T, typename std::invoke_result_t<Functor, T>::value_type, Functor, Derived>;

template <class B, class Functor>
requires FoldFunctor<B, T, Functor>
[[nodiscard]] auto fold(B&& init, Functor&& f) -> B;
Expand Down Expand Up @@ -277,6 +286,17 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::filter(Functor&& f)
return Filter<T, Functor, Derived>{std::forward<Derived>(self()), std::forward<Functor>(f)};
}

template <class T, class Derived>
template <class Functor>
requires rusty_iterators::concepts::FilterMapFunctor<
T, typename std::invoke_result_t<Functor, T>::value_type, Functor>
auto rusty_iterators::interface::IterInterface<T, Derived>::filterMap(Functor&& f)
-> FilterMap<T, typename std::invoke_result_t<Functor, T>::value_type, Functor, Derived>
{
return FilterMap<T, typename std::invoke_result_t<Functor, T>::value_type, Functor, Derived>{
std::forward<Derived>(self()), std::forward<Functor>(f)};
}

template <class T, class Derived>
template <class B, class Functor>
requires rusty_iterators::concepts::FoldFunctor<B, T, Functor>
Expand Down
33 changes: 33 additions & 0 deletions tests/filter_map.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <rusty_iterators/iterator.hpp>

using ::rusty_iterators::iterator::LazyIterator;

TEST(TestFilterMapIterator, NextChecksIfValid)
{
auto vec = std::vector{1, 2, 3};
auto f = [](auto x) -> std::optional<float> {
if (x % 2 == 0)
return std::make_optional(static_cast<float>(x));
return std::nullopt;
};
auto it = LazyIterator{vec}.filterMap(std::move(f));

ASSERT_EQ(it.next(), 2.0);
ASSERT_EQ(it.next(), std::nullopt);
}

TEST(TestFilterMapIterator, SizeHintReturnsValueOfUnderlyingIterator)
{
auto vec = std::vector{1, 2, 3};
auto f = [](auto x) -> std::optional<float> {
if (x % 2 == 0)
return std::make_optional(static_cast<float>(x));
return std::nullopt;
};
auto it = LazyIterator{vec}.filterMap(std::move(f));

ASSERT_EQ(it.sizeHint(), 3.0);
}