More consistent use of error_code. socket::last_error() now returns a std::error_code, while socket::last_errno() returns platform-specific error.

This commit is contained in:
fpagliughi
2023-04-28 17:55:00 -04:00
parent 0974f860fe
commit 3a6083e8bf
16 changed files with 86 additions and 76 deletions

View File

@@ -70,7 +70,7 @@ int main(int argc, char* argv[])
cout << "Created a connection from " << conn.address() << endl;
// Set a timeout for the responses
if (!conn.read_timeout(seconds(5))) {
if (!conn.read_timeout(5s)) {
cerr << "Error setting timeout on TCP stream: "
<< conn.last_error_str() << endl;
}

View File

@@ -55,9 +55,9 @@ int main(int argc, char* argv[])
sockpp::initialize();
// Implicitly creates an inet_address from {host,port}
// and then tries the connection.
// and then tries the connection using a timeout of 5sec.
sockpp::tcp_connector conn({host, port}, seconds{5});
sockpp::tcp_connector conn({host, port}, 5s);
if (!conn) {
cerr << "Error connecting to server at "
<< sockpp::inet_address(host, port)
@@ -68,7 +68,7 @@ int main(int argc, char* argv[])
cout << "Created a connection from " << conn.address() << endl;
// Set a timeout for the responses
if (!conn.read_timeout(seconds(5))) {
if (!conn.read_timeout(5s)) {
cerr << "Error setting timeout on TCP stream: "
<< conn.last_error_str() << endl;
}

View File

@@ -101,7 +101,7 @@ int main(int argc, char* argv[])
string s, sret;
while (getline(cin, s) && !s.empty()) {
if (conn.write(s) != (int) s.length()) {
if (conn.last_error() == EPIPE) {
if (conn.last_error() == errc::broken_pipe) {
cerr << "It appears that the socket was closed." << endl;
}
else {

View File

@@ -80,7 +80,7 @@ class result {
* This should be called after a failed system call to get the cause of
* the error.
*/
static int get_last_error() {
static int get_last_errno() {
#if defined(_WIN32)
return ::WSAGetLastError();
#else
@@ -89,6 +89,16 @@ class result {
#endif
}
/**
* Retrieves the last error from an operation.
* This should be called after a failed system call to get the cause of
* the error.
*/
static std::error_code get_last_error() {
int ec = get_last_errno();
return error_code{ ec, std::system_category() };
}
friend class socket;
public:
@@ -148,6 +158,16 @@ public:
static result from_error(errc err) {
return result(err);
}
/**
* Creates an unsuccessful result from an platform-specific integer
* error code and an optional category.
* @param ec The platform-specific error code.
* @param ecat The error category.
* @return The result of an unsuccessful operation.
*/
static result from_last_error() {
return from_error(get_last_errno());
}
/**
* Determines if the result represents a failed operation.
*

View File

@@ -206,15 +206,6 @@ protected:
close(release());
return false;
}
/**
* OS-specific means to retrieve the last error from an operation.
* This should be called after a failed system call to set the
* lastErr_ member variable. Normally this would be called from
* @ref check_ret.
*/
static int get_last_error() {
return ioresult::get_last_error();
}
/**
* Cache the last system error code into this object.
* This should be called after a failed system call to store the error
@@ -222,7 +213,7 @@ protected:
* @return The error value set.
*/
int set_last_error() const {
return lastErr_ = get_last_error();
return lastErr_ = ioresult::get_last_errno();
}
/**
* Checks the value and if less than zero, sets last error.
@@ -232,7 +223,7 @@ protected:
*/
template <typename T>
T check_ret(T ret) const {
lastErr_ = (ret < 0) ? get_last_error() : 0;
lastErr_ = (ret < 0) ? ioresult::get_last_errno() : 0;
return ret;
}
/**
@@ -244,7 +235,7 @@ protected:
*/
template <typename T>
bool check_ret_bool(T ret) const {
lastErr_ = (ret < 0) ? get_last_error() : 0;
lastErr_ = (ret < 0) ? ioresult::get_last_errno() : 0;
return ret >= 0;
}
/**
@@ -271,7 +262,7 @@ protected:
* @return Returns the value sent to it, `ret`.
*/
socket_t check_socket(socket_t ret) const {
lastErr_ = (ret == INVALID_SOCKET) ? get_last_error() : 0;
lastErr_ = (ret == INVALID_SOCKET) ? ioresult::get_last_errno() : 0;
return ret;
}
/**
@@ -283,8 +274,8 @@ protected:
* @return @em true if the value is a valid socket (not INVALID_SOCKET)
* or @em false is is an error (INVALID_SOCKET)
*/
bool check_socket_bool(socket_t ret) const{
lastErr_ = (ret == INVALID_SOCKET) ? get_last_error() : 0;
bool check_socket_bool(socket_t ret) const {
lastErr_ = (ret == INVALID_SOCKET) ? ioresult::get_last_errno() : 0;
return ret != INVALID_SOCKET;
}
/**
@@ -579,7 +570,17 @@ public:
* This is typically the code from the underlying OS operation.
* @return The code for the last errror.
*/
int last_error() const { return lastErr_; }
std::error_code last_error() const {
return std::error_code{ lastErr_, std::system_category() };
}
/**
* Gets the platform-specific errror from the last failed operation.
* This is integer the code from the OS:
* @li On *nix systems, this is the `errno` for the current thread.
* @li On Windows, this is the value returned by `WSAGetLastError()`.
* @return The platform-specific for the last errror.
*/
int last_errno() const { return lastErr_; }
/**
* Gets a string describing the last errror.
* This is typically the returned message from the system strerror().

View File

@@ -71,8 +71,8 @@ protected:
* Creates a streaming socket.
* @return An OS handle to a stream socket.
*/
static socket_t create_handle(int domain) {
return (socket_t) ::socket(domain, COMM_TYPE, 0);
static socket_t create_handle(int domain, int protocol=0) {
return (socket_t) ::socket(domain, COMM_TYPE, protocol);
}
public:

View File

@@ -47,7 +47,7 @@ acceptor acceptor::create(int domain)
{
acceptor acc(create_handle(domain));
if (!acc)
acc.clear(get_last_error());
acc.set_last_error();
return acc;
}

View File

@@ -36,17 +36,7 @@
#include "sockpp/connector.h"
#include <cerrno>
#if defined(_WIN32)
// Winsock calls return non-POSIX error codes
#undef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#undef ETIMEDOUT
#define ETIMEDOUT WSAETIMEDOUT
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#else
#if !defined(_WIN32)
#include <sys/poll.h>
#if defined(__APPLE__)
#include <net/if.h>
@@ -87,7 +77,7 @@ bool connector::connect(const sock_address& addr)
/////////////////////////////////////////////////////////////////////////////
bool connector::connect(const sock_address& addr, std::chrono::microseconds timeout)
bool connector::connect(const sock_address& addr, microseconds timeout)
{
if (timeout.count() <= 0)
return connect(addr);
@@ -105,10 +95,9 @@ bool connector::connect(const sock_address& addr, std::chrono::microseconds time
if (!non_blocking)
set_non_blocking(true);
// TODO: Reimplement with poll() for systems with lots of sockets.
if (!check_ret_bool(::connect(handle(), addr.sockaddr_ptr(), addr.size()))) {
if (last_error() == EINPROGRESS || last_error() == EWOULDBLOCK) {
auto err = last_error();
if (err == errc::operation_in_progress || err == errc::operation_would_block) {
// TODO: Windows has a WSAPoll() function we can use.
#if defined(_WIN32)
// Non-blocking connect -- call `select` to wait until the timeout:
@@ -138,7 +127,7 @@ bool connector::connect(const sock_address& addr, std::chrono::microseconds time
}
}
if (last_error() != 0) {
if (last_error()) {
close();
return false;
}

View File

@@ -112,7 +112,7 @@ socket socket::create(int domain, int type, int protocol /*=0*/)
{
socket sock(::socket(domain, type, protocol));
if (!sock)
sock.clear(get_last_error());
sock.set_last_error();
return sock;
}
@@ -140,7 +140,7 @@ socket socket::clone() const
int socket::get_flags() const
{
int flags = ::fcntl(handle_, F_GETFL, 0);
lastErr_ = (flags == -1) ? get_last_error() : 0;
lastErr_ = (flags == -1) ? ioresult::get_last_errno() : 0;
return flags;
}
@@ -187,7 +187,7 @@ std::tuple<socket, socket> socket::pair(int domain, int type, int protocol /*=0*
sock1.reset(sv[1]);
}
else {
int err = get_last_error();
int err = ioresult::get_last_errno();
sock0.clear(err);
sock1.clear(err);
}

View File

@@ -49,9 +49,9 @@ namespace sockpp {
stream_socket stream_socket::create(int domain, int protocol /*=0*/)
{
stream_socket sock(::socket(domain, COMM_TYPE, protocol));
stream_socket sock(create_handle(domain, protocol));
if (!sock)
sock.clear(get_last_error());
sock.set_last_error();
return sock;
}
@@ -94,7 +94,7 @@ ssize_t stream_socket::read_n(void *buf, size_t n)
uint8_t *b = reinterpret_cast<uint8_t*>(buf);
while (nr < n) {
if ((nx = read(b+nr, n-nr)) < 0 && last_error() == EINTR)
if ((nx = read(b + nr, n - nr)) < 0 && last_error() == errc::interrupted)
continue;
if (nx <= 0)
@@ -201,7 +201,7 @@ ssize_t stream_socket::write_n(const void *buf, size_t n)
const uint8_t *b = reinterpret_cast<const uint8_t*>(buf);
while (nw < n) {
if ((nx = write(b+nw, n-nw)) < 0 && last_error() == EINTR)
if ((nx = write(b + nw, n - nw)) < 0 && last_error() == errc::interrupted)
continue;
if (nx <= 0)

View File

@@ -6,7 +6,7 @@
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2019 Frank Pagliughi
// Copyright (c) 2019-2023 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -65,7 +65,7 @@ TEST_CASE("acceptor handle constructor", "[acceptor]") {
REQUIRE(!sock);
REQUIRE(!sock.is_open());
// TODO: Should this set an error?
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
}
}
@@ -76,7 +76,7 @@ TEST_CASE("acceptor address constructor", "[acceptor]") {
acceptor sock(ADDR);
REQUIRE(sock);
REQUIRE(sock.is_open());
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
REQUIRE(sock.address() == ADDR);
}
@@ -91,7 +91,7 @@ TEST_CASE("acceptor address constructor", "[acceptor]") {
#if defined(_WIN32)
REQUIRE(sock.last_error() == WSAEINVAL);
#else
REQUIRE(sock.last_error() == EAFNOSUPPORT);
REQUIRE(sock.last_error() == errc::address_family_not_supported);
#endif
}
}
@@ -102,7 +102,7 @@ TEST_CASE("acceptor create", "[acceptor]") {
REQUIRE(sock);
REQUIRE(sock.is_open());
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
// Windows returns unknown family for unbound socket
// Windows returns a different error code than *nix
@@ -123,7 +123,7 @@ TEST_CASE("acceptor create", "[acceptor]") {
#if defined(_WIN32)
REQUIRE(sock.last_error() == WSAEINVAL);
#else
REQUIRE(sock.last_error() == EAFNOSUPPORT);
REQUIRE(sock.last_error() == errc::address_family_not_supported);
#endif
}
}

View File

@@ -59,7 +59,7 @@ TEST_CASE("connector unspecified address", "[connector]") {
#if defined(_WIN32)
REQUIRE(conn.last_error() == WSAENOTSOCK);
#else
REQUIRE(conn.last_error() == EAFNOSUPPORT);
REQUIRE(conn.last_error() == errc::address_family_not_supported);
#endif
}

View File

@@ -6,7 +6,7 @@
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2019 Frank Pagliughi
// Copyright (c) 2019-2023 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -65,7 +65,7 @@ TEST_CASE("datagram_socket handle constructor", "[datagram_socket]") {
REQUIRE(!sock);
REQUIRE(!sock.is_open());
// TODO: Should this set an error?
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
}
}
@@ -76,7 +76,7 @@ TEST_CASE("datagram_socket address constructor", "[datagram_socket]") {
datagram_socket sock(ADDR);
REQUIRE(sock);
REQUIRE(sock.is_open());
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
REQUIRE(sock.address() == ADDR);
}
@@ -91,7 +91,7 @@ TEST_CASE("datagram_socket address constructor", "[datagram_socket]") {
#if defined(_WIN32)
REQUIRE(sock.last_error() == WSAEINVAL);
#else
REQUIRE(sock.last_error() == EAFNOSUPPORT);
REQUIRE(sock.last_error() == errc::address_family_not_supported);
#endif
}
}

View File

@@ -6,7 +6,7 @@
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2019 Frank Pagliughi
// Copyright (c) 2019-2023 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -87,7 +87,7 @@ TEST_CASE("socket constructors", "[socket]") {
REQUIRE(!sock);
REQUIRE(!sock.is_open());
REQUIRE(sock.handle() == INVALID_SOCKET);
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
}
SECTION("handle constructor") {
@@ -97,7 +97,7 @@ TEST_CASE("socket constructors", "[socket]") {
REQUIRE(sock);
REQUIRE(sock.is_open());
REQUIRE(sock.handle() == HANDLE);
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
}
SECTION("move constructor") {
@@ -109,7 +109,7 @@ TEST_CASE("socket constructors", "[socket]") {
// Make sure the new socket got the handle
REQUIRE(sock);
REQUIRE(sock.handle() == HANDLE);
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
// Make sure the handle was moved out of the org_sock
REQUIRE(!org_sock);
@@ -131,19 +131,19 @@ TEST_CASE("socket errors", "[socket]") {
REQUIRE(!ok);
REQUIRE(!sock);
int err = sock.last_error();
REQUIRE(err != 0);
auto err = sock.last_error();
REQUIRE(err);
// last_error() is sticky, unlike `errno`
REQUIRE(sock.last_error() == err);
// We can clear the error
sock.clear();
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
// Test arbitrary clear value
sock.clear(42);
REQUIRE(sock.last_error() == 42);
REQUIRE(sock.last_error().value() == 42);
REQUIRE(!sock);
}
@@ -270,7 +270,7 @@ TEST_CASE("failed socket pair", "[socket]") {
REQUIRE(!sock1);
REQUIRE(!sock2);
REQUIRE(sock1.last_error() != 0);
REQUIRE(sock1.last_error());
REQUIRE(sock1.last_error() == sock2.last_error());
}
@@ -311,7 +311,7 @@ TEST_CASE("thread-safe last error", "[socket]") {
std::thread thr([&] {
// Test #1
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
{
// Wait for Test #2
std::unique_lock<std::mutex> lk(m);
@@ -321,7 +321,7 @@ TEST_CASE("thread-safe last error", "[socket]") {
}
// Test #3
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
});
{
@@ -337,7 +337,7 @@ TEST_CASE("thread-safe last error", "[socket]") {
bool ok = sock.get_option(SOL_SOCKET, SO_REUSEADDR, &reuse, &len);
REQUIRE(!ok);
REQUIRE(sock.last_error() != 0);
REQUIRE(sock.last_error());
{
std::unique_lock<std::mutex> lk(m);

View File

@@ -6,7 +6,7 @@
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2019 Frank Pagliughi
// Copyright (c) 2019-2023 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -65,7 +65,7 @@ TEST_CASE("stream_socket handle constructor", "[stream_socket]") {
REQUIRE(!sock);
REQUIRE(!sock.is_open());
// TODO: Should this set an error?
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
}
}
@@ -77,7 +77,7 @@ TEST_CASE("stream_socket address constructor", "[stream_socket]") {
stream_socket sock(ADDR);
REQUIRE(sock);
REQUIRE(sock.is_open());
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
REQUIRE(sock.address() == ADDR);
}
@@ -87,7 +87,7 @@ TEST_CASE("stream_socket address constructor", "[stream_socket]") {
stream_socket sock(ADDR);
REQUIRE(!sock);
REQUIRE(!sock.is_open());
REQUIRE(sock.last_error() == EAFNOSUPPORT);
REQUIRE(sock.last_error() == errc::address_family_not_supported);
}
}
#endif

View File

@@ -72,7 +72,7 @@ TEST_CASE("tcp_socket handle constructor", "[tcp_socket]") {
REQUIRE(!sock);
REQUIRE(!sock.is_open());
// TODO: Should this set an error?
REQUIRE(sock.last_error() == 0);
REQUIRE(!sock.last_error());
//REQUIRE(sock.family() == AF_INET);
}