Skip to content

Commit

Permalink
add zip_transform adaptor
Browse files Browse the repository at this point in the history
  • Loading branch information
YarikTH committed Aug 15, 2023
1 parent 6d00cc6 commit 3fe7aa3
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 7 deletions.
7 changes: 5 additions & 2 deletions include/ureact/adaptor/zip.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ struct ZipAdaptor : Adaptor
{
static_assert( sizeof...( Sources ) >= 1, "zip: 2+ arguments are required" );

using node_type = event_zip_node<unit, Source, Sources...>;
using E = typename node_type::E;

const context& context = source1.get_context();
return detail::create_wrapped_node<events<std::tuple<Source, Sources...>>,
event_zip_node<Source, Sources...>>( context, source1, sources... );
return detail::create_wrapped_node<events<E>, node_type>(
context, unit{}, source1, sources... );
}
};

Expand Down
52 changes: 52 additions & 0 deletions include/ureact/adaptor/zip_transform.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// Copyright (C) 2014-2017 Sebastian Jeckel.
// Copyright (C) 2020-2023 Krylov Yaroslav.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef UREACT_ADAPTOR_ZIP_TRANSFORM_HPP
#define UREACT_ADAPTOR_ZIP_TRANSFORM_HPP

#include <ureact/detail/adaptor.hpp>
#include <ureact/detail/zip_base.hpp>

UREACT_BEGIN_NAMESPACE

namespace detail
{

struct ZipTransformAdaptor : Adaptor
{
/*!
* @brief Emit an invoke result for func(e1,…,eN) for each complete set of values for sources 1...N
*
* Each source slot has its own unbounded buffer queue that persistently stores incoming events.
* For as long as all queues are not empty, one value is popped from each and emitted together as a tuple.
*
* Semantically equivalent of ranges::zip_transform
*/
template <typename F, typename Source, typename... Sources>
UREACT_WARN_UNUSED_RESULT constexpr auto operator()(
F&& func, const events<Source>& source1, const events<Sources>&... sources ) const
{
static_assert( sizeof...( Sources ) >= 1, "zip: 2+ arguments are required" );

using node_type = event_zip_node<F, Source, Sources...>;
using E = typename node_type::E;

const context& context = source1.get_context();
return detail::create_wrapped_node<events<E>, node_type>(
context, std::forward<F>( func ), source1, sources... );
}
};

} // namespace detail

inline constexpr detail::ZipTransformAdaptor zip_transform;

UREACT_END_NAMESPACE

#endif //UREACT_ADAPTOR_ZIP_TRANSFORM_HPP
34 changes: 29 additions & 5 deletions include/ureact/detail/zip_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define UREACT_DETAIL_ZIP_BASE_HPP

#include <deque>
#include <functional>
#include <tuple>

#include <ureact/detail/defines.hpp>
Expand All @@ -21,12 +22,29 @@ UREACT_BEGIN_NAMESPACE
namespace detail
{

template <typename... Values>
class event_zip_node final : public event_stream_node<std::tuple<Values...>>
template <typename F, typename... Values>
struct zip_result
{
using func_type = std::conditional_t< //
std::is_same_v<F, unit>, //
decltype( std::make_tuple<Values...> ),
F>;
using type = std::invoke_result_t<func_type, Values...>;
};

template <typename F, typename... Values>
using zip_result_t = typename zip_result<F, Values...>::type;

template <typename F, typename... Values>
class event_zip_node final : public event_stream_node<zip_result_t<F, Values...>>
{
public:
explicit event_zip_node( const context& context, const events<Values>&... sources )
using E = zip_result_t<F, Values...>;

template <typename InF>
explicit event_zip_node( const context& context, InF&& func, const events<Values>&... sources )
: event_zip_node::event_stream_node( context )
, m_func( std::forward<InF>( func ) )
, m_slots( sources... )
{
this->attach_to( sources... );
Expand All @@ -44,8 +62,13 @@ class event_zip_node final : public event_stream_node<std::tuple<Values...>>
auto& events = this->get_events();

while( are_all_slots_ready() )
pop_slots_to_emit_event( [&events]( Values&&... values ) { //
events.emplace_back( std::forward<Values>( values )... );
pop_slots_to_emit_event( [this, &events]( Values&&... values ) { //
std::ignore = this; // [[maybe_unused]] analog for lambda capture

if constexpr( std::is_same_v<F, unit> )
events.emplace_back( std::forward<Values>( values )... );
else
events.push_back( std::invoke( m_func, std::forward<Values>( values )... ) );
} );

return !this->get_events().empty() ? update_result::changed : update_result::unchanged;
Expand Down Expand Up @@ -118,6 +141,7 @@ class event_zip_node final : public event_stream_node<std::tuple<Values...>>
m_slots );
}

F m_func;
std::tuple<slot<Values>...> m_slots;
};

Expand Down
1 change: 1 addition & 0 deletions tests/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ target_sources(
adaptor/unique.cpp
adaptor/values.cpp
adaptor/zip.cpp
adaptor/zip_transform.cpp
feature/adaptor.cpp
feature/default_context.cpp
feature/has_changed.cpp
Expand Down
37 changes: 37 additions & 0 deletions tests/src/adaptor/zip_transform.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Copyright (C) 2020-2023 Krylov Yaroslav.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#include "ureact/adaptor/zip_transform.hpp"

#include "catch2_extra.hpp"
#include "ureact/adaptor/collect.hpp"
#include "ureact/events.hpp"

// zip 3 event sources into 1
// based on example https://en.cppreference.com/w/cpp/ranges/zip_transform_view
TEST_CASE( "ureact::zip_transform" )
{
ureact::context ctx;

auto v1 = ureact::make_source<float>( ctx );
auto v2 = ureact::make_source<short>( ctx );
auto v3 = ureact::make_source<int>( ctx );

const auto add = []( auto a, auto b, auto c ) { return a + b + c; };

ureact::events<float> src = ureact::zip_transform( add, v1, v2, v3 );

const auto result = ureact::collect<std::vector>( src );

// clang-format off
v1 << 1 << 2 << 3;
v2 << 1 << 2 << 3 << 4;
v3 << 1 << 2 << 3 << 4 << 5;
// clang-format on

CHECK( result.get() == std::vector<float>{ 3, 6, 9 } );
}

0 comments on commit 3fe7aa3

Please sign in to comment.