Boost logo

Boost :

From: Christopher Kohlhoff (chris_at_[hidden])
Date: 2006-12-02 07:09:30


Hi Oleg,

On Sat, 02 Dec 2006 17:44:46 +0600, "Oleg Abrosimov" <olegabr_at_[hidden]>
said:
> This reminds me the motivation for the Null Object pattern:
> The object has a trivial state that should be checked explicitly in
> every method. It is often better to apply the degenerated case of the
> State pattern (The Null Object pattern) and eliminate all this annoying
> checks in object's methods.
>
> If we apply this to asio's async* methods, we should provide two
> handlers: first for success case and second for fail, for example:
>
> socket_.async_connect(endpoint,
> boost::bind(&chat_client::handle_connect, this), // success
> boost::bind(&chat_client::handle_connect_error, this, // fail
> boost::asio::placeholders::error, ++endpoint_iterator));

This was discussed during the review of asio. It very much depends on
the particular use case whether is any benefit in having two handlers,
and it's not always as simple as choosing between error and success
either. The proposed solution (which hopefully I will get around to
adding soon) was to create a split_error adapter that allows two
handlers to be plugged in to handle the different cases. Here's a sketch
of how it would be implemented:

  template <typename Handler1, typename Handler2>
  class split_error_t
  {
  public:
    split_error_t(Handler1 h1, Handler2 h2) : h1_(h1), h2_(h2) {}

    void operator()(const error_code& ec)
    {
      if (ec) h2_(ec) else h1_(ec);
    }

    template <typename A1, ... typename AN>
    void operator()(const error_code& ec, A1 a1, ... AN an)
    {
      if (ec) h2_(ec, a1, ... an) else h1_(ec, a1, ... an);
    }

  private:
    Handler1 h1_;
    Handler2 h2_;
  };

  template <typename Handler1, typename Handler2>
  split_error_t<Handler1, Handler2>
  split_error(Handler1 h1, Handler2 h2)
  {
    return split_error_t<Handler1, Handler2>(h1, h2);
  }

Then async_connect could then be written as:

  socket_.async_connect(endpoint,
      split_error(
        boost::bind(&chat_client::handle_connect, this), // success
        boost::bind(&chat_client::handle_connect_error, this, // fail
          boost::asio::placeholders::error, ++endpoint_iterator)));

I prefer keeping a single handler on the async operations for reasons of
genericity. Splitting the handlers on error/success is only one possible
way. Another might be an N-way split based on a bunch of different error
codes (e.g. to have different handlers per error code). Adapters can be
used here to cater for all sorts of different scenarios.

Cheers,
Chris


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk