Finished adding per-operation error handler support. It is now only invoked

in case of error, never on success.
This commit is contained in:
chris 2003-11-11 08:08:58 +00:00
parent cc58f99de7
commit e667e7ce1e
15 changed files with 642 additions and 153 deletions

View File

@ -90,9 +90,9 @@ public:
* @param address An address on the local machine to which the dgram socket
* will be bound.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -150,9 +150,9 @@ public:
* @param address An address on the local machine to which the dgram socket
* will be bound.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -207,9 +207,9 @@ public:
*
* @param option The new option value to be set on the socket.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -240,9 +240,9 @@ public:
*
* @param option The option value to be obtained from the socket.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -288,9 +288,9 @@ public:
*
* @param destination The remote address to which the data will be sent.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -413,9 +413,9 @@ public:
* @param sender_address An address object that receives the address of the
* remote sender of the datagram.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode

View File

@ -97,9 +97,9 @@ public:
* @param listen_queue The maximum length of the queue of pending
* connections. A value of 0 means use the default queue length.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -163,9 +163,9 @@ public:
* @param listen_queue The maximum length of the queue of pending
* connections. A value of 0 means use the default queue length.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -220,9 +220,9 @@ public:
*
* @param option The new option value to be set on the acceptor.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -255,9 +255,9 @@ public:
*
* @param option The option value to be obtained from the acceptor.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -295,9 +295,9 @@ public:
* @param peer_socket The stream socket into which the new connection will be
* accepted.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
@ -394,9 +394,9 @@ public:
* @param peer_address An address object which will receive the network
* address of the remote peer.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode

View File

@ -21,6 +21,7 @@
#include <boost/noncopyable.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/error_handler.hpp"
#include "asio/null_completion_context.hpp"
#include "asio/service_factory.hpp"
@ -131,7 +132,34 @@ public:
template <typename Stream, typename Address>
void connect(Stream& peer_socket, const Address& peer_address)
{
service_.connect(impl_, peer_socket.lowest_layer(), peer_address);
service_.connect(impl_, peer_socket.lowest_layer(), peer_address,
default_error_handler());
}
/// Connect a stream socket to the peer at the specified address.
/**
* This function is used to connect a stream socket to the specified remote
* address. The function call will block until the connection is successfully
* made or an error occurs.
*
* @param peer_socket The stream socket to be connected.
*
* @param peer_address The remote address of the peer to which the socket
* will be connected.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/
template <typename Stream, typename Address, typename Error_Handler>
void connect(Stream& peer_socket, const Address& peer_address,
Error_Handler error_handler)
{
service_.connect(impl_, peer_socket.lowest_layer(), peer_address,
error_handler);
}
/// Start an asynchronous connect.

View File

@ -21,6 +21,7 @@
#include <boost/noncopyable.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/error_handler.hpp"
#include "asio/null_completion_context.hpp"
#include "asio/service_factory.hpp"
@ -139,7 +140,26 @@ public:
template <typename Option>
void set_option(const Option& option)
{
service_.set_option(impl_, option);
service_.set_option(impl_, option, default_error_handler());
}
/// Set an option on the socket.
/**
* This function is used to set an option on the socket.
*
* @param option The new option value to be set on the socket.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/
template <typename Option, typename Error_Handler>
void set_option(const Option& option, Error_Handler error_handler)
{
service_.set_option(impl_, option, error_handler);
}
/// Get an option from the socket.
@ -153,7 +173,26 @@ public:
template <typename Option>
void get_option(Option& option)
{
service_.get_option(impl_, option);
service_.get_option(impl_, option, default_error_handler());
}
/// Get an option from the socket.
/**
* This function is used to get the current value of an option on the socket.
*
* @param option The option value to be obtained from the socket.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/
template <typename Option, typename Error_Handler>
void get_option(Option& option, Error_Handler error_handler)
{
service_.get_option(impl_, option, error_handler);
}
/// Send the given data to the peer.
@ -177,7 +216,37 @@ public:
*/
size_t send(const void* data, size_t length)
{
return service_.send(impl_, data, length);
return service_.send(impl_, data, length, default_error_handler());
}
/// Send the given data to the peer.
/**
* This function is used to send data to the stream socket's peer. The
* function call will block until the data has been sent successfully or an
* error occurs.
*
* @param data The data to be sent to remote peer.
*
* @param length The size of the data to be sent, in bytes.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*
* @returns The number of bytes sent or 0 if the connection was closed
* cleanly.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the asio::send_n() function if you need to ensure that all
* data is sent before the blocking operation completes.
*/
template <typename Error_Handler>
size_t send(const void* data, size_t length, Error_Handler error_handler)
{
return service_.send(impl_, data, length, error_handler);
}
/// Start an asynchronous send.
@ -267,7 +336,38 @@ public:
*/
size_t recv(void* data, size_t max_length)
{
return service_.recv(impl_, data, max_length);
return service_.recv(impl_, data, max_length, default_error_handler());
}
/// Receive some data from the peer.
/**
* This function is used to receive data from the stream socket's peer. The
* function call will block until data has received successfully or an error
* occurs.
*
* @param data The buffer into which the received data will be written.
*
* @param max_length The maximum size of the data to be received, in bytes.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*
* @returns The number of bytes received or 0 if the connection was closed
* cleanly.
*
* @note The recv operation may not receive all of the requested number of
* bytes. Consider using the asio::recv_n() function if you need to ensure
* that the requested amount of data is received before the blocking
* operation completes.
*/
template <typename Error_Handler>
size_t recv(void* data, size_t max_length, Error_Handler error_handler)
{
return service_.recv(impl_, data, max_length, error_handler);
}
/// Start an asynchronous receive.

View File

@ -94,6 +94,14 @@ public:
return next_layer_.send(data, length);
}
/// Send the given data to the peer. Returns the number of bytes sent or 0 if
/// the stream was closed cleanly.
template <typename Error_Handler>
size_t send(const void* data, size_t length, Error_Handler error_handler)
{
return next_layer_.send(data, length, error_handler);
}
/// Start an asynchronous send. The data being sent must be valid for the
/// lifetime of the asynchronous operation.
template <typename Handler>
@ -125,6 +133,22 @@ public:
return buffer_.size() - previous_size;
}
/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation, or 0 if the underlying connection
/// was closed.
template <typename Error_Handler>
size_t fill(Error_Handler error_handler)
{
detail::buffer_resize_guard<Buffer> resize_guard(buffer_);
size_t previous_size = buffer_.size();
buffer_.resize(buffer_.capacity());
buffer_.resize(previous_size + next_layer_.recv(
buffer_.begin() + previous_size, buffer_.size() - previous_size,
error_handler));
resize_guard.commit();
return buffer_.size() - previous_size;
}
template <typename Handler>
class fill_handler
{
@ -180,6 +204,16 @@ public:
return copy(data, max_length);
}
/// Receive some data from the peer. Returns the number of bytes received or
/// 0 if the stream was closed cleanly.
template <typename Error_Handler>
size_t recv(void* data, size_t max_length, Error_Handler error_handler)
{
if (buffer_.empty() && !fill(error_handler))
return 0;
return copy(data, max_length);
}
template <typename Handler>
class recv_handler
{

View File

@ -80,6 +80,14 @@ public:
return next_layer_.send(data, length);
}
/// Send the given data to the peer. Returns the number of bytes sent or 0 if
/// the stream was closed cleanly.
template <typename Error_Handler>
size_t send(const void* data, size_t length, Error_Handler error_handler)
{
return next_layer_.send(data, length, error_handler);
}
/// Start an asynchronous send. The data being sent must be valid for the
/// lifetime of the asynchronous operation.
template <typename Handler>
@ -104,6 +112,14 @@ public:
return next_layer_.recv(data, max_length);
}
/// Receive some data from the peer. Returns the number of bytes received or
/// 0 if the stream was closed cleanly.
template <typename Error_Handler>
size_t recv(void* data, size_t max_length, Error_Handler error_handler)
{
return next_layer_.recv(data, max_length, error_handler);
}
/// Start an asynchronous receive. The buffer for the data being received
/// must be valid for the lifetime of the asynchronous operation.
template <typename Handler>

View File

@ -90,6 +90,14 @@ public:
return stream_impl_.send(data, length);
}
/// Send the given data to the peer. Returns the number of bytes sent or 0 if
/// the stream was closed cleanly.
template <typename Error_Handler>
size_t send(const void* data, size_t length, Error_Handler error_handler)
{
return stream_impl_.send(data, length, error_handler);
}
/// Start an asynchronous send. The data being sent must be valid for the
/// lifetime of the asynchronous operation.
template <typename Handler>
@ -115,6 +123,15 @@ public:
return stream_impl_.fill();
}
/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation, or 0 if the underlying connection
/// was closed.
template <typename Error_Handler>
size_t fill(Error_Handler error_handler)
{
return stream_impl_.fill(error_handler);
}
/// Start an asynchronous fill.
template <typename Handler>
void async_fill(Handler handler)
@ -136,6 +153,14 @@ public:
return stream_impl_.recv(data, max_length);
}
/// Receive some data from the peer. Returns the number of bytes received or
/// 0 if the stream was closed cleanly.
template <typename Error_Handler>
size_t recv(void* data, size_t max_length, Error_Handler error_handler)
{
return stream_impl_.recv(data, max_length, error_handler);
}
/// Start an asynchronous receive. The buffer for the data being received
/// must be valid for the lifetime of the asynchronous operation.
template <typename Handler>

View File

@ -82,8 +82,6 @@ public:
}
impl = sock.release();
error_handler(socket_error(socket_error::success));
}
// Destroy a dgram socket implementation.
@ -103,12 +101,7 @@ public:
{
if (socket_ops::setsockopt(impl, option.level(), option.name(),
option.data(), option.size()))
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
}
// Set a socket option.
@ -118,12 +111,7 @@ public:
socket_len_type size = option.size();
if (socket_ops::getsockopt(impl, option.level(), option.name(),
option.data(), &size))
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
}
// Send a datagram to the specified address. Returns the number of bytes
@ -139,8 +127,6 @@ public:
error_handler(socket_error(socket_ops::get_error()));
return 0;
}
error_handler(socket_error(socket_error::success));
return bytes_sent;
}
@ -217,8 +203,6 @@ public:
sender_address.native_size(addr_len);
error_handler(socket_error(socket_error::success));
return bytes_recvd;
}

View File

@ -92,8 +92,6 @@ public:
}
impl = sock.release();
error_handler(socket_error(socket_error::success));
}
// Destroy a stream socket implementation.
@ -113,12 +111,7 @@ public:
{
if (socket_ops::setsockopt(impl, option.level(), option.name(),
option.data(), option.size()))
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
}
// Set a socket option.
@ -128,12 +121,7 @@ public:
socket_len_type size = option.size();
if (socket_ops::getsockopt(impl, option.level(), option.name(),
option.data(), &size))
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
}
// Accept a new connection.
@ -157,8 +145,6 @@ public:
}
peer.set_impl(new_socket);
error_handler(socket_error(socket_error::success));
}
// Accept a new connection.
@ -187,8 +173,6 @@ public:
peer_address.native_size(addr_len);
peer.set_impl(new_socket);
error_handler(socket_error(socket_error::success));
}
template <typename Stream_Socket_Service, typename Handler,

View File

@ -119,23 +119,29 @@ public:
}
}
// Connect the given socket to the peer at the specified address. Throws a
// socket_error exception on error.
template <typename Stream_Socket_Service, typename Address>
// Connect the given socket to the peer at the specified address.
template <typename Stream_Socket_Service, typename Address,
typename Error_Handler>
void connect(impl_type& impl,
basic_stream_socket<Stream_Socket_Service>& peer,
const Address& peer_address)
const Address& peer_address, Error_Handler error_handler)
{
// We cannot connect a socket that is already open.
if (peer.impl() != invalid_socket)
throw socket_error(socket_error::already_connected);
{
error_handler(socket_error(socket_error::already_connected));
return;
}
// Create a new socket for the connection. This will not be put into the
// stream_socket object until the connection has beenestablished.
socket_holder sock(socket_ops::socket(peer_address.family(), SOCK_STREAM,
IPPROTO_TCP));
if (sock.get() == invalid_socket)
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
// Perform the connect operation itself.
impl->add_socket(sock.get());
@ -143,7 +149,10 @@ public:
peer_address.native_size());
impl->remove_socket(sock.get());
if (result == socket_error_retval)
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
// Connection was successful. The stream_socket object will now take
// ownership of the newly connected native socket handle.

View File

@ -72,33 +72,38 @@ public:
}
}
// Set a socket option. Throws a socket_error exception on failure.
template <typename Option>
void set_option(impl_type& impl, const Option& option)
// Set a socket option.
template <typename Option, typename Error_Handler>
void set_option(impl_type& impl, const Option& option,
Error_Handler error_handler)
{
if (socket_ops::setsockopt(impl, option.level(), option.name(),
option.data(), option.size()))
throw socket_error(socket_ops::get_error());
error_handler(socket_error(socket_ops::get_error()));
}
// Set a socket option. Throws a socket_error exception on failure.
template <typename Option>
void get_option(impl_type& impl, Option& option)
// Set a socket option.
template <typename Option, typename Error_Handler>
void get_option(impl_type& impl, Option& option, Error_Handler error_handler)
{
socket_len_type size = option.size();
if (socket_ops::getsockopt(impl, option.level(), option.name(),
option.data(), &size))
throw socket_error(socket_ops::get_error());
error_handler(socket_error(socket_ops::get_error()));
}
// Send the given data to the peer. Returns the number of bytes sent or
// 0 if the connection was closed cleanly. Throws a socket_error exception
// on failure.
size_t send(impl_type& impl, const void* data, size_t length)
// 0 if the connection was closed cleanly.
template <typename Error_Handler>
size_t send(impl_type& impl, const void* data, size_t length,
Error_Handler error_handler)
{
int bytes_sent = socket_ops::send(impl, data, length, 0);
if (bytes_sent < 0)
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return 0;
}
return bytes_sent;
}
@ -154,13 +159,17 @@ public:
}
// Receive some data from the peer. Returns the number of bytes received or
// 0 if the connection was closed cleanly. Throws a socket_error exception
// on failure.
size_t recv(impl_type& impl, void* data, size_t max_length)
// 0 if the connection was closed cleanly.
template <typename Error_Handler>
size_t recv(impl_type& impl, void* data, size_t max_length,
Error_Handler error_handler)
{
int bytes_recvd = socket_ops::recv(impl, data, max_length, 0);
if (bytes_recvd < 0)
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return 0;
}
return bytes_recvd;
}

View File

@ -78,33 +78,49 @@ public:
}
}
// Set a socket option. Throws a socket_error exception on failure.
template <typename Option>
void set_option(impl_type& impl, const Option& option)
// Set a socket option.
template <typename Option, typename Error_Handler>
void set_option(impl_type& impl, const Option& option,
Error_Handler error_handler)
{
if (socket_ops::setsockopt(impl, option.level(), option.name(),
option.data(), option.size()))
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
}
// Set a socket option. Throws a socket_error exception on failure.
template <typename Option>
void get_option(impl_type& impl, Option& option)
// Set a socket option.
template <typename Option, typename Error_Handler>
void get_option(impl_type& impl, Option& option, Error_Handler error_handler)
{
socket_len_type size = option.size();
if (socket_ops::getsockopt(impl, option.level(), option.name(),
option.data(), &size))
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
}
// Send the given data to the peer. Returns the number of bytes sent or
// 0 if the connection was closed cleanly. Throws a socket_error exception
// on failure.
size_t send(impl_type& impl, const void* data, size_t length)
// 0 if the connection was closed cleanly.
template <typename Error_Handler>
size_t send(impl_type& impl, const void* data, size_t length,
Error_Handler error_handler)
{
int bytes_sent = socket_ops::send(impl, data, length, 0);
if (bytes_sent < 0)
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return 0;
}
error_handler(socket_error(socket_error::success));
return bytes_sent;
}
@ -178,13 +194,18 @@ public:
}
// Receive some data from the peer. Returns the number of bytes received or
// 0 if the connection was closed cleanly. Throws a socket_error exception
// on failure.
size_t recv(impl_type& impl, void* data, size_t max_length)
// 0 if the connection was closed cleanly.
template <typename Error_Handler>
size_t recv(impl_type& impl, void* data, size_t max_length,
Error_Handler error_handler)
{
int bytes_recvd = socket_ops::recv(impl, data, max_length, 0);
if (bytes_recvd < 0)
throw socket_error(socket_ops::get_error());
{
error_handler(socket_error(socket_ops::get_error()));
return 0;
}
error_handler(socket_error(socket_error::success));
return bytes_recvd;
}

View File

@ -37,28 +37,28 @@ class expression
{
public:
/// Constructor.
explicit expression(Expr expr)
explicit expression(Expr expr)
: expr_(expr)
{
}
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
return expr_(err);
}
private:
/// The contained expression.
Expr expr_;
Expr expr_;
};
/// Create an expression object using template type deduction.
template <typename Expr>
expression<Expr> make_expression(Expr expr)
{
return expression<Expr>(expr);
return expression<Expr>(expr);
}
/// Class template to compare the error for equality with a given value.
@ -67,35 +67,35 @@ class value_eq_error
{
public:
/// Constructor.
explicit value_eq_error(Value value)
explicit value_eq_error(Value value)
: value_(value)
{
}
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
return value_ == err;
}
private:
/// The value to compare the error against.
Value value_;
Value value_;
};
/// Compare the error for equality with a given value.
template <typename Value>
expression<value_eq_error<Value> > operator==(Value value, error_t)
{
return make_expression(value_eq_error<Value>(value));
return make_expression(value_eq_error<Value>(value));
}
/// Compare the error for equality with a given value.
template <typename Value>
expression<value_eq_error<Value> > operator==(error_t, Value value)
{
return make_expression(value_eq_error<Value>(value));
return make_expression(value_eq_error<Value>(value));
}
/// Class template to compare the error for inequality with a given value.
@ -104,35 +104,35 @@ class value_neq_error
{
public:
/// Constructor.
explicit value_neq_error(Value value)
explicit value_neq_error(Value value)
: value_(value)
{
}
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
return value_ != err;
}
private:
/// The value to compare the error against.
Value value_;
Value value_;
};
/// Compare the error for inequality with a given value.
template <typename Value>
expression<value_neq_error<Value> > operator!=(Value value, error_t)
{
return make_expression(value_neq_error<Value>(value));
return make_expression(value_neq_error<Value>(value));
}
/// Compare the error for inequality with a given value.
template <typename Value>
expression<value_neq_error<Value> > operator!=(error_t, Value value)
{
return make_expression(value_neq_error<Value>(value));
return make_expression(value_neq_error<Value>(value));
}
/// Class template to perform logical or on two expressions.
@ -141,7 +141,7 @@ class expr_or_expr
{
public:
/// Constructor.
explicit expr_or_expr(Expr1 expr1, Expr2 expr2)
explicit expr_or_expr(Expr1 expr1, Expr2 expr2)
: expr1_(expr1),
expr2_(expr2)
{
@ -149,17 +149,17 @@ public:
// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
return expr1_(err) || expr2_(err);
}
private:
/// The first expression to be included in the logical or.
Expr1 expr1_;
Expr1 expr1_;
/// The second expression to be included in the logical or.
Expr2 expr2_;
Expr2 expr2_;
};
/// Perform a logical or on two expressions.
@ -167,7 +167,7 @@ template <typename Expr1, typename Expr2>
expression<expr_or_expr<expression<Expr1>, expression<Expr2> > >
operator||(expression<Expr1> expr1, expression<Expr2> expr2)
{
return make_expression(
return make_expression(
expr_or_expr<expression<Expr1>, expression<Expr2> >(expr1, expr2));
}
@ -177,7 +177,7 @@ class expr_and_expr
{
public:
/// Constructor.
explicit expr_and_expr(Expr1 expr1, Expr2 expr2)
explicit expr_and_expr(Expr1 expr1, Expr2 expr2)
: expr1_(expr1),
expr2_(expr2)
{
@ -185,17 +185,17 @@ public:
// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
return expr1_(err) && expr2_(err);
}
private:
/// The first expression to be included in the logical and.
Expr1 expr1_;
Expr1 expr1_;
/// The second expression to be included in the logical and.
Expr2 expr2_;
Expr2 expr2_;
};
/// Perform a logical and on two expressions.
@ -203,7 +203,7 @@ template <typename Expr1, typename Expr2>
expression<expr_and_expr<expression<Expr1>, expression<Expr2> > >
operator&&(expression<Expr1> expr1, expression<Expr2> expr2)
{
return make_expression(
return make_expression(
expr_and_expr<expression<Expr1>, expression<Expr2> >(expr1, expr2));
}
@ -213,7 +213,7 @@ class throw_error
public:
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
throw err;
}
@ -225,14 +225,14 @@ class throw_error_if_t
{
public:
/// Constructor.
throw_error_if_t(Expr expr)
throw_error_if_t(Expr expr)
: expr_(expr)
{
}
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
if (expr_(err))
throw err;
@ -241,14 +241,14 @@ public:
private:
/// The expression which, if true, will result in the error being thrown.
Expr expr_;
Expr expr_;
};
/// Throw an error if an expression is true.
template <typename Expr>
expression<throw_error_if_t<Expr> > throw_error_if(Expr expr)
{
return make_expression(throw_error_if_t<Expr>(expr));
return make_expression(throw_error_if_t<Expr>(expr));
}
/// Class template to always set a variable to the error.
@ -264,7 +264,7 @@ public:
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
target_ = err;
return true;
@ -279,7 +279,9 @@ private:
template <typename Target>
expression<set_error_t<Target> > set_error(Target& target)
{
return make_expression(set_error_t<Target>(target));
if (target)
target = Target();
return make_expression(set_error_t<Target>(target));
}
/// Class template to set a variable to the error if an expression is true.
@ -288,7 +290,7 @@ class set_error_if_t
{
public:
/// Constructor.
set_error_if_t(Target& target, Expr expr)
set_error_if_t(Target& target, Expr expr)
: target_(target),
expr_(expr)
{
@ -296,7 +298,7 @@ public:
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
if (expr_(err))
{
@ -311,7 +313,7 @@ private:
Target& target_;
/// The expression which, if true, will result in the variable being set.
Expr expr_;
Expr expr_;
};
/// Set a variable to the error if an expression is true.
@ -319,7 +321,9 @@ template <typename Target, typename Expr>
expression<set_error_if_t<Target, Expr> >
set_error_if(Target& target, Expr expr)
{
return make_expression(set_error_if_t<Target, Expr>(target, expr));
if (target)
target = Target();
return make_expression(set_error_if_t<Target, Expr>(target, expr));
}
/// Class template to always log an error to a stream.
@ -335,7 +339,7 @@ public:
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
ostream_ << err << "\n";
return true;
@ -350,7 +354,7 @@ private:
template <typename Ostream>
expression<log_error_t<Ostream> > log_error(Ostream& ostream)
{
return make_expression(log_error_t<Ostream>(ostream));
return make_expression(log_error_t<Ostream>(ostream));
}
/// Class template to set a variable to the error if an expression is true.
@ -359,7 +363,7 @@ class log_error_if_t
{
public:
/// Constructor.
log_error_if_t(Ostream& ostream, Expr expr)
log_error_if_t(Ostream& ostream, Expr expr)
: ostream_(ostream),
expr_(expr)
{
@ -367,7 +371,7 @@ public:
/// Evaluate the expression.
template <typename Error>
bool operator()(const Error& err)
bool operator()(const Error& err)
{
if (expr_(err))
{
@ -382,7 +386,7 @@ private:
Ostream& ostream_;
/// The expression which, if true, will result in the variable being set.
Expr expr_;
Expr expr_;
};
/// Set a variable to the error if an expression is true.
@ -390,7 +394,7 @@ template <typename Ostream, typename Expr>
expression<log_error_if_t<Ostream, Expr> >
log_error_if(Ostream& ostream, Expr expr)
{
return make_expression(log_error_if_t<Ostream, Expr>(ostream, expr));
return make_expression(log_error_if_t<Ostream, Expr>(ostream, expr));
}
/// The default error handler.

View File

@ -54,6 +54,41 @@ size_t recv(Stream& s, void* data, size_t max_length)
return s.recv(data, max_length);
}
/// Read some data from a stream.
/**
* This function is used to receive data on a stream. The function call will
* block until data has received successfully or an error occurs.
*
* @param s The stream on which the data is to be received.
*
* @param data The buffer into which the received data will be written.
*
* @param max_length The maximum size of the data to be received, in bytes.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code template <typename Error>
* void error_handler(
* const Error& error // Result of operation (the actual type is dependent on
* // the underlying stream's recv operation)
* ); @endcode
*
* @returns The number of bytes received, or 0 if end-of-file was reached or
* the connection was closed cleanly.
*
* @note The recv operation may not receive all of the requested number of
* bytes. Consider using the asio::recv_n() function if you need to ensure that
* the requested amount of data is received before the blocking operation
* completes.
*/
template <typename Stream, typename Error_Handler>
size_t recv(Stream& s, void* data, size_t max_length,
Error_Handler error_handler)
{
return s.recv(data, max_length, error_handler);
}
/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive data on a stream. The
@ -172,6 +207,56 @@ size_t recv_n(Stream& s, void* data, size_t length,
return bytes_recvd;
}
/// Read the specified amount of data from the stream before returning.
/**
* This function is used to receive an exact number of bytes of data on a
* stream. The function call will block until the specified number of bytes has
* been received successfully or an error occurs.
*
* @param s The stream on which the data is to be received.
*
* @param data The buffer into which the received data will be written.
*
* @param length The size of the data to be received, in bytes.
*
* @param total_bytes_recvd An optional output parameter that receives the
* total number of bytes actually received.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code template <typename Error>
* void error_handler(
* const Error& error // Result of operation (the actual type is dependent on
* // the underlying stream's recv operation)
* ); @endcode
*
* @returns The number of bytes received on the last recv, or 0 if end-of-file
* was reached or the connection was closed cleanly.
*/
template <typename Stream, typename Error_Handler>
size_t recv_n(Stream& s, void* data, size_t length,
size_t* total_bytes_recvd, Error_Handler error_handler)
{
int bytes_recvd = 0;
size_t total_recvd = 0;
while (total_recvd < length)
{
bytes_recvd = recv(s, static_cast<char*>(data) + total_recvd,
length - total_recvd, error_handler);
if (bytes_recvd == 0)
{
if (total_bytes_recvd)
*total_bytes_recvd = total_recvd;
return bytes_recvd;
}
total_recvd += bytes_recvd;
}
if (total_bytes_recvd)
*total_bytes_recvd = total_recvd;
return bytes_recvd;
}
namespace detail
{
#if defined(_MSC_VER)
@ -365,6 +450,75 @@ size_t recv_decode(Buffered_Stream& s, Decoder decoder,
}
}
/// Read some data from a stream and decode it.
/**
* This function is used to receive data on a stream and decode it in a single
* operation. The function call will block until the decoder function object
* indicates that it has finished.
*
* @param s The stream on which the data is to be received.
*
* @param decoder The decoder function object to be called to decode the
* received data. The function object is assumed to be stateful. That is, it
* may not be given sufficient data in a single invocation to complete
* decoding, and is expected to maintain state so that it may resume decoding
* when the next piece of data is supplied. Copies will be made of the decoder
* function object as required, however with respect to maintaining state it
* can rely on the fact that only an up-to-date copy will be used. The
* equivalent function signature of the handler must be:
* @code std::pair<bool, const char*> decoder(
* const char* begin, // Pointer to the beginning of data to be decoded.
* const char* end // Pointer to one-past-the-end of data to be decoded.
* ); @endcode
* The first element of the return value is true if the decoder has finished.
* The second element is a pointer to the beginning of the unused portion of
* the data.
*
* @param total_bytes_recvd An optional output parameter that receives the
* total number of bytes actually received.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code template <typename Error>
* void error_handler(
* const Error& error // Result of operation (the actual type is dependent on
* // the underlying stream's recv operation)
* ); @endcode
*
* @returns The number of bytes received on the last recv, or 0 if end-of-file
* was reached or the connection was closed cleanly.
*/
template <typename Buffered_Stream, typename Decoder, typename Error_Handler>
size_t recv_decode(Buffered_Stream& s, Decoder decoder,
size_t* total_bytes_recvd, Error_Handler error_handler)
{
size_t total_recvd = 0;
for (;;)
{
if (s.recv_buffer().empty() && s.fill(error_handler) == 0)
{
if (total_bytes_recvd)
*total_bytes_recvd = total_recvd;
return 0;
}
std::pair<bool, const char*> result =
decoder(s.recv_buffer().begin(), s.recv_buffer().end());
size_t bytes_read = result.second - s.recv_buffer().begin();
s.recv_buffer().pop(bytes_read);
total_recvd += bytes_read;
if (result.first)
{
if (total_bytes_recvd)
*total_bytes_recvd = total_recvd;
return bytes_read;
}
}
}
namespace detail
{
#if defined(_MSC_VER)
@ -641,6 +795,43 @@ size_t recv_until(Buffered_Stream& s, std::string& data,
total_bytes_recvd);
}
/// Read data from the stream until a delimiter is reached.
/**
* This function is used to receive data from the stream into a std::string
* object until a specified delimiter is reached. The function call will block
* until the delimiter is found or an error occurs.
*
* @param s The stream on which the data is to be received.
*
* @param data The std::string object into which the received data will be
* written.
*
* @param delimiter The pattern marking the end of the data to receive.
*
* @param total_bytes_recvd An optional output parameter that receives the
* total number of bytes actually received.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code template <typename Error>
* void error_handler(
* const Error& error // Result of operation (the actual type is dependent on
* // the underlying stream's recv operation)
* ); @endcode
*
* @returns The number of bytes received on the last recv, or 0 if end-of-file
* was reached or the connection was closed cleanly.
*/
template <typename Buffered_Stream, typename Error_Handler>
size_t recv_until(Buffered_Stream& s, std::string& data,
const std::string& delimiter, size_t* total_bytes_recvd,
Error_Handler error_handler)
{
return recv_decode(s, detail::recv_until_decoder(data, delimiter),
total_bytes_recvd, error_handler);
}
/// Start an asynchronous receive that will not complete until the specified
/// delimiter is encountered.
/**

View File

@ -48,6 +48,40 @@ size_t send(Stream& s, const void* data, size_t length)
return s.send(data, length);
}
/// Write some data to a stream.
/**
* This function is used to send data on a stream. The function call will block
* until the data has been sent successfully or an error occurs.
*
* @param s The stream on which the data is to be sent.
*
* @param data The data to be sent on the stream.
*
* @param length The size of the data to be sent, in bytes.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code template <typename Error>
* void error_handler(
* const Error& error // Result of operation (the actual type is dependent on
* // the underlying stream's send operation)
* ); @endcode
*
* @returns The number of bytes sent, or 0 if end-of-file was reached or the
* connection was closed cleanly.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the asio::send_n() function if you need to ensure that all
* data is sent before the blocking operation completes.
*/
template <typename Stream, typename Error_Handler>
size_t send(Stream& s, const void* data, size_t length,
Error_Handler error_handler)
{
return s.send(data, length, error_handler);
}
/// Start an asynchronous send.
/**
* This function is used to asynchronously send data on a stream. The function
@ -164,6 +198,56 @@ size_t send_n(Stream& s, const void* data, size_t length,
return bytes_sent;
}
/// Write all of the given data to the stream before returning.
/**
* This function is used to send an exact number of bytes of data on a stream.
* The function call will block until the specified number of bytes has been
* sent successfully or an error occurs.
*
* @param s The stream on which the data is to be sent.
*
* @param data The data to be sent on the stream.
*
* @param length The size of the data to be sent, in bytes.
*
* @param total_bytes_sent An optional output parameter that receives the
* total number of bytes actually sent.
*
* @param error_handler The handler to be called when an error occurs. Copies
* will be made of the handler as required. The equivalent function signature
* of the handler must be:
* @code template <typename Error>
* void error_handler(
* const Error& error // Result of operation (the actual type is dependent on
* // the underlying stream's send operation)
* ); @endcode
*
* @returns The number of bytes sent on the last send, or 0 if end-of-file was
* reached or the connection was closed cleanly.
*/
template <typename Stream, typename Error_Handler>
size_t send_n(Stream& s, const void* data, size_t length,
size_t* total_bytes_sent, Error_Handler error_handler)
{
int bytes_sent = 0;
size_t total_sent = 0;
while (total_sent < length)
{
bytes_sent = send(s, static_cast<const char*>(data) + total_sent,
length - total_sent, error_handler);
if (bytes_sent == 0)
{
if (total_bytes_sent)
*total_bytes_sent = total_sent;
return bytes_sent;
}
total_sent += bytes_sent;
}
if (total_bytes_sent)
*total_bytes_sent = total_sent;
return bytes_sent;
}
namespace detail
{
#if defined(_MSC_VER)