Another real-world example of using reinterpret_cast
is using the various network related functions that accept struct sockaddr *
parameter, namely recvfrom()
, bind()
or accept()
.
For example, following is the definition of the recvfrom function:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
Its fifth argument is defined as struct sockaddr *src_addr
and it acts as a general interface for accepting a pointer to a structure of a specific address type (for example, sockaddr_in
or sockaddr_in6
).
The Beej's Guide to Network Programming says:
In memory, the struct sockaddr_in and struct sockaddr_in6 share the
same beginning structure as struct sockaddr, and you can freely cast
the pointer of one type to the other without any harm, except the
possible end of the universe.
Just kidding on that end-of-the-universe thing…if the universe does
end when you cast a struct sockaddr_in* to a struct sockaddr*, I
promise you it’s pure coincidence and you shouldn’t even worry about
it.
So, with that in mind, remember that whenever a function says it takes
a struct sockaddr* you can cast your struct sockaddr_in*, struct
sockaddr_in6*, or struct sockadd_storage* to that type with ease and
safety.
For example:
int fd; // file descriptor value obtained elsewhere
struct sockaddr_in addr {};
socklen_t addr_len = sizeof(addr);
std::vector<std::uint8_t> buffer(4096);
const int bytes_recv = recvfrom(fd, buffer.data(), buffer.size(), 0,
reinterpret_cast<sockaddr*>(&addr), &addr_len);