Use std::span()
Opened this issue · 13 comments
I like library interface very much, but it should use std::span(void *)/std::span(const void *) in lot of methods instead of pair (void *buf, size_t n)/(const void *buf, size_t n)
Hey, thanks for the suggestion. I know after 10 years this still claims to be a "modern" C++ library, but it is actually compatible with C++14. I still have a lot of legacy projects that use it, and they will likely never have a compiler upgrade. I was considering updating to C++17 in a near-future release, but no plans for C++20 at the moment.
It doesn't seem worth it to phase out a lot of legacy and embedded code for small changes like pair vs span, even if they would be nice. But maybe some conditional compilation based on the compiler version? I don't know if that would get confusing.
Ha. It's been a while since I was deep into this library, that I forget the implementation details! I grep'ed through the code and it seems I'n not using std::pair<> anywhere in the library.
The term "pair" is used in the library to return to a pair of sockets derived from the C socket library call socketpair()
:
https://man7.org/linux/man-pages/man2/socketpair.2.html
Those are actually returned in C++ in a 2-element tuple, std::tuple<socket,socket>
- not a std::pair<>
.
I think span wouldn't work here since it's a non-owning container, right?
I think best way is to provide you own simplified implementation (only with std::size_t Extent = std::dynamic_extent
) for C++ 14- C++17 that have same interface with std::span, but when C++ compiler support C++20 you could use instead alias for std::span. We use this approach in our project.
Or use span from MS GSL https://github.com/microsoft/GSL/blob/main/include/gsl/span
I think span wouldn't work here since it's a non-owning container, right?
Yes, span is not owning. It just array slice :), but with helper methods that allow to get sub-span that often very helpful
Could you give me an example, specifically, of what you think should be changed?
class stream_socket : public socket
{
....
virtual ssize_t read(std::span<void> buf);
virtual ioresult read_r(std::span<void> buf);
virtual ssize_t read_n(std::span<void> buf);
virtual ioresult read_n_r(std::span<void> buf);
virtual ssize_t write(std::span<const void> buf);
virtual ioresult write_r(std::span<const void> buf);
virtual ssize_t write_n(std::span<const void> buf);
virtual ioresult write_n_r(std::span<const void> buf);
};
So you could use
sockpp::tcp_connector conn({host, port});
std::array<char, 10> buffer;
conn.write(buffer);
or
conn.write(std::span(buffer).first(3)); // write part of buffer
Oh, OK. Now I get you.
I would not want to break backward compatibility to replace the existing functions, but maybe we can add those with conditional compilation for C++20?
class stream_socket : public socket
{
...
virtual ssize_t read(void *buf, size_t n);
virtual ioresult read_r(void *buf, size_t n);
virtual ssize_t read_n(void *buf, size_t n);
...
#if __cplusplus >= 202002L
virtual ssize_t read(std::span<void> buf) { return read(buf.data(), buf.size()); }
virtual ioresult read_r(std::span<void> buf) { return read_r(buf.data(), buf.size()); }
virtual ssize_t read_n(std::span<void> buf) { return read_n(buf.data(), buf.size()); }
...
#endif
};
Yeah, it's ok. But probably also support ms GSL span implementation for C++14-17.
BTW read
should user std::span<const void>
:)
I could see it maybe as an opt-in CMake build option. It's not something I would have the time or inclination to do in the near future, but if you send over a PR, I'd give it a look.
std::span<void>
isn't valid. The proper way to do this stuff in C++17 is std::span<std::byte>
. std::byte
is the "correct" type to use for collections of bytes.
Yes, of course you're right. It should not be a span of void
!
But honestly, I'm not yet convinced of std::byte
especially for collections of data (as opposed to individual bit fields for machine register, etc).. I'm still in the uint8_t
camp.
But that also shows the problem with using something like span<>
for this. How easy/difficult is it to convert from different array and collection types? Is that assumed you can/should do that with a span of bytes? The problem is that a void*
means "anything", but a void
means "nothing". What a language.
This is exactly what std::byte
is for. Its underlying type is unsigned char
and is not a character type or an arithmetic type.
I guess std::span<std::byte>
is locked in more than void*
. Definitely would be an API break. People are too used to void*
for sockets.