Skip to content
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

[discussion] Implementing drop_last for pipes #61

Open
kunaltyagi opened this issue Oct 11, 2020 · 2 comments
Open

[discussion] Implementing drop_last for pipes #61

kunaltyagi opened this issue Oct 11, 2020 · 2 comments

Comments

@kunaltyagi
Copy link

If a pipe is allowed to store the values locally in a circular queue, it can accumulate the first N values in it, and then release the rest.

Assuming an interface like Boost.CircularBuffer for the circular queue

class drop_last
{
  circular_buffer<std::any> queue_;

public:
  template <typename... Values, typename TailPipeline>
  void onReceive(Values&&... values, TailPipeline&& tailPipeline)
  {
    using DataStore = std::tuple<Values...>;
    if(queue_.full()) {
      send(SOMEHOW_FORWARD_OR_MOVE(std::any_cast<DataStore>(queue_.front()), FWD(tailPipeline));
    }
    queue_.push_back(std::make_any<DataStore>(values...));
  }

  explicit drop_last(std::size_t nbToDrop) queue_(nbToDrop) {}
};

Can it be made more efficient?

  • If the size is 1, we can use std::exchange to efficiently move the values. If the size is constant, we could use a circular buffer on stack (eg: with a backend on std::array instead of std::vector)
  • If the circular buffer implementation supports a std::exchange like operation, queue_.front() and queue_.push_back could be made one single operation
  • If the drop_last was templated instead of onReceive, there would be no need of using std::any

Big issue: SOMEHOW_FORWARD_OR_MOVE

@joboccara
Copy link
Owner

Thanks for the idea.
Doesn't this implementation assume that all the (tuples of) values coming successively to the pipe are of the same type?

@kunaltyagi
Copy link
Author

kunaltyagi commented Oct 12, 2020

Doesn't this implementation assume that all the (tuples of) values coming successively to the pipe are of the same type?

Yes, but I feel that this is a "reasonably" safe (and in case of input coming from containers, guaranteed) assumption (what's a few throws among friends)

Perhaps, this limitation can be made explicit by templating the class itself (and thus removing the need for std::any)

Or else, it'd need tuple + lambda shenanigans to do it:

// the template part
template <class T, class... V>
struct exchange_result {
  T old;
  std::tuple<V...> tuple;
};

template <class Value, class V0, class...V>
auto tuple_exchange(std::tuple<V0, V...>&& existing, Value&& in) {
  auto a = std::move(std::get<0>(existing));
  return exchange_result{a, 
  std::make_tuple<V..., Value>(std::move(std::get<V>(existing))..., in)};
}

I am not sure if what I wish to do with lambda is legal (or possible), but essentially, I wanted to use onReceive to store a lambda in a std::function. The lambda acts the storage for the tuple and the queue logic, while returning a new lambda for the onReceive function to store (and delete the original).

EDIT:
If we force the interface to have a constant type (or std::any) we can have really nice interface

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants