Skip to content

Instantly share code, notes, and snippets.

@Tosainu
Created May 20, 2023 14:55
Show Gist options
  • Save Tosainu/84a68ac10d575e9466a77c2c0f238ba7 to your computer and use it in GitHub Desktop.
Save Tosainu/84a68ac10d575e9466a77c2c0f238ba7 to your computer and use it in GitHub Desktop.
#include <concepts>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
extern "C" {
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
}
namespace nyan {
class file_descriptor {
int fd_;
public:
file_descriptor() noexcept : fd_{-1} {};
explicit file_descriptor(int fd) noexcept : fd_{fd} {};
file_descriptor(const file_descriptor&) = delete;
file_descriptor& operator=(const file_descriptor&) = delete;
file_descriptor(file_descriptor&& fd) noexcept : fd_{std::exchange(fd.fd_, -1)} {}
file_descriptor& operator=(file_descriptor&& fd) noexcept {
if (fd_ >= 0) {
::close(fd_);
}
fd_ = std::exchange(fd.fd_, -1);
return *this;
}
int raw_fd() const noexcept {
return fd_;
}
operator bool() const noexcept {
return fd_ >= 0;
}
};
template <class S>
struct socket_base : public file_descriptor {
static constexpr int domain = S::domain;
using file_descriptor::file_descriptor;
socket_base(int type, int protocol) noexcept
: file_descriptor{::socket(domain, type, protocol)} {}
};
template <class T>
concept Address = requires(T addr) //
{
typename T::native_type;
{ addr.addr } -> std::same_as<std::add_lvalue_reference_t<typename T::native_type>>;
};
template <class T>
concept Socket = requires(const T sock) //
{
typename T::endpoint_type;
requires Address<typename T::endpoint_type>;
};
template <Socket S>
inline auto listen(const socket_base<S>& socket, int backlog) {
return ::listen(socket.raw_fd(), backlog);
}
template <Socket S>
inline auto bind(const socket_base<S>& socket, const typename S::endpoint_type& addr) {
return ::bind(socket.raw_fd(), reinterpret_cast<const ::sockaddr*>(&addr.addr),
sizeof(typename S::endpoint_type::native_type));
}
template <Socket S>
inline auto accept(const socket_base<S>& socket, typename S::endpoint_type& addr, int flags = 0) {
::socklen_t len = sizeof(typename S::endpoint_type::native_type);
return S{::accept4(socket.raw_fd(), reinterpret_cast<::sockaddr*>(&addr.addr), &len, flags)};
}
template <Socket S>
inline auto connect(const socket_base<S>& socket, const typename S::endpoint_type& addr) {
return ::connect(socket.raw_fd(), reinterpret_cast<const ::sockaddr*>(&addr.addr),
sizeof(typename S::endpoint_type::native_type));
}
namespace af_inet {
struct endpoint {
using native_type = ::sockaddr_in;
native_type addr;
};
struct socket : public socket_base<socket> {
static constexpr int domain = AF_INET;
using endpoint_type = endpoint;
using socket_base::socket_base;
};
} // namespace af_inet
namespace af_unix {
struct endpoint {
using native_type = ::sockaddr_un;
native_type addr;
};
struct socket : public socket_base<socket> {
static constexpr int domain = AF_UNIX;
using endpoint_type = endpoint;
using socket_base::socket_base;
};
} // namespace af_unix
} // namespace nyan
auto main() -> int {
auto s = nyan::af_inet::socket{SOCK_STREAM, 0};
bind(s, nyan::af_inet::endpoint{});
listen(s, 20);
for (;;) {
nyan::af_inet::endpoint peer{};
auto ss = accept(s, peer, SOCK_CLOEXEC);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment